C++赋值运算符函数
编写assigment赋值运算符函数时需注意几点:
1. 返回值类型声明为该class的引用,为允许连续赋值(如可解析为右结合律的a=b=c),函数结束前应返回对象自身引用(*this);
2. 传入参数类型应声明为该class的常量引用,因为1)如果传入的参数是实例,从形参到实参会调用一次赋值构造函数增大开销 2)加上const关键字避免改变传入参数对应实例;
3. 注意释放该实例自身已有的内存,如果分配新内存前忘记释放已有的内存会造成内存泄漏;
4. 应先判断传入参数对应的实例和*this是否为同一实例,如果相同则直接返回*this,不进行赋值操作。如果对与*this相同的实例进行赋值操作,释放自身内存时也同时会释放传入参数的内存,造成严重问题。
经典解法:先判断传入参数是否为*this,若不是则先释放已有内存,再根据传入的参数分配新内存并对数据进行赋值,返回*this。
CMyString& CMyString::operator=(const CMyString& 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; }
但此解法仍不具备异常安全性,如果因为分配时内存不足或copy函数抛出异常导致new()异常,持有指针可能会指向已被删除的对象。为让赋值运算符具备异常安全性,可在复制指针所指对象时,先不delete该指针,这样如果new()抛出异常,指针和指针原指的对象能够保持原状。
CMyString& CMyString::operator=(const CMyString& str) { if(this==&str) return *this; char* pOrig = m_pData; m_pData = new char[strlen(str.m_pData)+1]; strcpy(m_pData, str.m_pData); delete pOrig; return *this; }
此外,还有一个办法是先创建一个临时实例,再交换临时实例和原来的实例,因为临时实例是一个局部变量,程序运行到区块结束就离开了该变量的作用域,临时变量的析构函数被自动调用,其指针指向的内存会被释放,也就释放了原先的m_pData指向的内存。
CMyString& CMyString::operator=(const CMyString& str) { if(this == &str) return *this; CMyString strTemp(str); char* pTemp = strTemp.m_pData; strTemp.m_pData = m_pData; m_pData = pTemp; return *this; }
完整代码:
#include <iostream> #include <stdio.h> using namespace std; class CMyString { public: CMyString(char* pData = NULL); CMyString(const CMyString& str); ~CMyString(void); CMyString& operator=(const CMyString& str); void print(); private: char* m_pData; }; CMyString::CMyString(char* pData) { if(pData == NULL) { m_pData = new char[1]; m_pData[0] = '\0'; } else { int length = strlen(pData); m_pData = new char[length+1]; strcpy(m_pData, pData); } } CMyString::CMyString(const CMyString& str) { int length = strlen(str.m_pData); m_pData = new char[length+1]; strcpy(m_pData, str.m_pData); } CMyString::~CMyString(void) {delete []m_pData;} CMyString& CMyString::operator=(const CMyString& str) { if(this == &str) return *this; CMyString strTemp(str); char* pTemp = strTemp.m_pData; strTemp.m_pData = m_pData; m_pData = pTemp; return *this; } void CMyString::print() { cout << "The data is "; printf("%s\n", m_pData); } int main() { char* text="sdlkjfskdfk"; CMyString str1(text); cout << "str1: "; str1.print(); CMyString str2(str1); cout << "-------do str2(str1)--------"<< endl; cout << "str2: "; str2.print(); cout << "------------------------------" << endl << endl; CMyString str3="4534534534dd"; cout << "str1: "; str1.print(); cout << "str2: "; str2.print(); cout << "str3: "; str3.print(); cout << "--------do str1 = str2 = str3 --------" << endl; str1 = str2 = str3; cout << "str1: "; str1.print(); cout << "str2: "; str2.print(); cout << "str3: "; str3.print(); cout << "---------------------------------" << endl << endl; cout << "str1: "; str1.print(); cout <<"------------do str1 = str1 --------" << endl; str1 = str1; cout << "str1: "; str1.print(); }
参考资料:《剑指offer名企面试官精讲编程题》