题目:如下类型CMyString的声明,请为该类添加赋值运算符函数
class CMyString
{
public:
CMyString(char* pdata = NULL);
CMyString(const CMyString& str);
//赋值函数
CMyString& operator=(const CMyString& str);
~CMyString();
private:
char* m_data;
};
面试官考察的点:
1、 是否把返回值的类型声明为该类型的引用,并在函数返回前,返回自身的引用(*this)。只有返回一个引用,才可以允许连续赋值。如果函数的返回值为void,则像str1=str2=str3这样的连续赋值将不会被通过
2、 是否把传入的参数类型声明为常引用。如果传入的参数不是引用而是实=实例的话,那么从形参到实参要进行一次拷贝构造函数的调用,把参数声明为引用,可以避免无谓的消耗,提高效率。同时,在赋值的时候,不应该改变传入参数的状态,所以,应该声明为常引用,加上const关键字
3、 是否释放自身的空间,否则程序将出现内存泄漏
4、 避免自赋值。判断传入的参数是否和当前的实例*this是一个实例,如果是同一个实例,则不进行赋值操作,直接返回。如果事先不进行判断,在释放自身实例的内存的时候就会出现问题,一旦释放了自身的内存,传入的参数的内存也同时释放了,因此再也找不到需要赋值的内容了
//初级程序员解法:
CMyString& CMyString::operator=(const CMyString& str)
{
//防止自赋值
if (&str == this)
return *this;
//释放自身空间
delete[] m_data;
m_data = NULL;
//分配空间,赋值
m_data = new char[strlen(str.m_data)+1];
strcpy(m_data, str.m_data);
//返回自身引用
return *this;
}
//考虑异常安全的解法,高级程序员必备
在前面的函数中,我们在分配内存之前先delete释放了自身的内存。但是如果此时内存不足,分配内存的时候抛出异常,那么m_data将是一个空指针,很容易导致程序崩溃,违背了异常安全原则
要想在赋值函数中实现异常安全性。有两种方法:1、一个简单的方法是先用new分配新内容,再delete释放已有的内容,这样在分配成功之后再释放原来的内容,也就是当分配失败时可以保证CMyString的实例不会被修改。2、还有一个更好的方法就是创建一个临时对象,再交换临时对象和原来对象
CMyString& CMyString::operator=(const CMyString& str)
{
if (&str != this)
{
//先通过拷贝构造函数创建一个临时对象
CMyString strtmp(str);
//交换临时对象和自身的m_data
char* pTmp = m_data;
m_data = strtmp.m_data;
strtmp.m_data = pTmp;
}
return *this;
}
这个函数中,先创建一个临时对象,然后交换临时对象和实例自身的m_data,由于临时对象是一个局部对象,出了if语句就会自动调用临时对象的析构函数,就会把临时对象所指的那块内存释放掉。
在这个新的代码中,我们在CMyString的构造函数里用new分配内存,如果内存不足抛出异常,这时,我们还没有修改原来实例的状态。就保证了异常安全性