赋值运算符函数
小小的一个赋值运算符函数的写法完全可以区分出一个程序猿的功底
运算符函数是C++中经典的运算符重载函数
对于给定一个类:
class my_string{public:my_string(char* pData=NULL);//带默认参数构造my_string(const my_string& str);//拷贝构造~my_string();//析构privat:char* m_pData;};
无论菜鸟程序猿还是高级攻城狮对于码一个赋值运算符函数,至少需要考虑到:
(1)是否把返回值的类型声明为该类型的引用
声明为引用的目的:只有返回引用,才可以连续赋值str1=str2=str3
(2)是否把传入的参数类型声明为常量引用
如果传入参数不是引用而是实例对象,调用过程中会再次调用拷贝构造函数,造成资源无畏消耗,降低代码效率,同时const关键字保证传入的参数不被改变,提高安全性
(3)需要判断传入参数和当前实例(*this)不是同一个实例
如果是同一个,则无需赋值操作,直接返回,如果不加判断,当传入参数和*this是同一个实例时,一旦释放自身内存会导致严重问题
(4)是否释放实例自身内存
开辟新空间,分配新内存之前需要释放自身空间,如果不释放造成内存泄漏
class my_string{public:my_string(char* pData=NULL);//带默认参数构造my_string(const my_string& str);//拷贝构造~my_string();//析构my_string& my_string::operator=(const my_string &str){if(this==&str){return* this;}delete[] m_pData;m_pData=NULL;m_pData=new char[strlen(str.m_pData)+1];strcpy(m_pData,str.m_pData);return *this;}privat:char* m_pData;};
上述代码,可能会出现安全问题:比如开辟新空间失败会导致m_pData为空,后续的拷贝都会失败,可以先分配新空间,等分配成功之后再释放原有内容
class my_string{public:my_string(char* pData=NULL);//带默认参数构造my_string(const my_string& str);//拷贝构造~my_string();//析构my_string& my_string::operator=(const my_string &str){if(this==&str){return* this;}char* tmp=m_pData;m_pData=new char[strlen(str.m_pData)+1];delete[] tmp;tmp=NULL;strcpy(m_pData,str.m_pData);return *this;}privat:char* m_pData;};
对于高级攻城狮而言,有一种更为高级的写法:
创建一个临时实例,将临时实例与自身实例(*this)进行交换
class my_string{public:my_string(char* pData=NULL);//带默认参数构造my_string(const my_string& str);//拷贝构造~my_string();//析构my_string& my_string::operator=(const my_string &str){if(this!=&str){my_string tmp(str);//调用拷贝构造swap(m_pData,tmp.m_pData);//实例交换}return *this;}privat:char* m_pData;};
上述代码,在创建临时对象实例的过程中,通过构造函数开辟新的内存空间,经过交换,临时实例对象指向原有内存空间,由于临时实例对象为局部变量,出作用域后,自动调用析构函数,释放原有内存空间。
简直就是一个字:妙!!!