30.赋值运算符重载函数[Assign copy constructor]

【问题】

给出如下CMyString的声明,要求为该类型添加赋值运算符函数。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
 

class CMyString
{
public:
    CMyString(
char *pData = NULL);
    CMyString(
const CMyString &str);
    ~CMyString(
void);

private:
    
char *m_pData;
};

分析】

当我们完整地考虑了上述几方面之后,我们可以写出如下的代码:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 

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;
}

面试官会提醒我们在前面的函数中,显示地用delete释放自身m_pData的内存。同时我们也会在析构函数中用delete释放自身m_pData的内存。如果这个类型中添加新的指针成员变量,那么我们至少需要做两处修改,即同时在析构函数和这个赋值运算符函数里添加一条delete语句来释放新指针所指向的内存。一个改动需要在代码中多个地方修改代码,通常是有安全隐患的。通常我们会记得在析构函数里用delete释放指针成员变量,但未必每次都记得到赋值运算符函数来添加代码释放内存。

更好的办法在复制运算符函数中利用析构函数自动释放实例已有的内存。

【代码】

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 

CMyString &CMyString::operator =(const CMyString &str)
{
    
if(this != &str)
    {
        CMyString strTemp(str);

        
char *pTemp = strTemp.m_pData;
        strTemp.m_pData = m_pData;
        m_pData = pTemp;
    }

    
return *this;
}

在这个函数中,我们定义一个临时实例strTemp,并把strTemp的m_pData指向当前实例(*this)的m_pData。由于strTemp是个局部变量,但程序员运行到if的外面是也就出了的该变量的域,就会自动调用strTemp的析构函数,就会把strTemp.m_pData所指向的内存释放掉。由于strTemp.m_pData指向的内存就是当前实例之前m_pData的内存。这就相当于自动调用析构函数释放当前实例的内存。如果新增加指针成员变量,我们只需要在析构函数里正确地释放,而不需要对赋值运算符函数做任何修改。

【参考】

http://zhedahht.blog.163.com/blog/static/25411174200741543224391/