面试题1:赋值运算符函数
题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。
class CMyString { public: CMyString(char* pData = NULL); CMyString(const CMyString&str); ~CMyString(void); private: char* m_pData; };
考点:
- 是否把返回值的类型声明为该类型的引用,并在函数结束前返回该实例自身的引用(*this)。只有这样才允许连续赋值。
- 是否把传入的参数的类型声明为常量引用。避免改变实例状态以及参数拷贝造成的无谓消耗。
- 是否释放实例自身已有的内存。避免内存泄露。
- 是否判断传入的参数和当前的实例是否为同一个实例。
初级程序员解法:
CMyString& CMyString::operator = (const CMyString&str) { if (this == &str) return *this; delete []m_pData; m_pData = NULL; m_pData = new char[strlen(str.mpData)+1]; strcpy(m_pData, str.m_pData); return *this; }
上述代码存在的问题:
先释放内存,再分配新的内存,一旦新的内存分配失败,则m_pData将是一个空指针,CMyString的实例不再保持有效状态,违背了异常安全性原则。
解决方法1:先判断内存是否分配成功,再释放原有内存。
解决方法2:先创建一个临时实例,再交换临时实例和原来的实例。
1 CMyString& CMyString::operator = (const CMyString&str) 2 { 3 if (this != &str) 4 { 5 CMyString strTemp(str); 6 char * temp = strTemp.m_pData; 7 strTemp.m_pData = m_pData; 8 m_pData = temp; 9 } 10 return *this; 11 }
分析:我们的目的是要释放原来内存,开辟新的内存,同时遵守异常安全性原则。在上述代码中,先创建一个临时变量strTemp,在此过程中会通过拷贝构造函数开辟新的内存,然后交换strTemp与m_pData的指针。由于strTemp是临时变量,作用域在if内,因此当程序运行到if的外面时就会调用该变量的析构函数释放strTemp.m_pData所指向的内存。同时,如果由于内存不足抛出异常,也不会对原来实例的状态产生影响,也就保证了异常安全性原则。