【面试题】 类的赋值运算符函数

题目:CMyString类型声明如下,请为其添加“赋值运算符”函数。

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

对于此类问题,面试官通常关注以下几点:

1. 返回值类型是否为引用,函数结束时是否“return *this;”。只有这样,才能允许CMyString类型对象的连续赋值。

2. 参数类型是否为常量引用。如果传入参数非引用而是实例,则从形参到实参会调用一次拷贝构造函数,造成性能损耗。赋值运算符函数内不会改变参数状态,因此应该为引用参数加上const关键字。

3. 是否释放m_pData之前所指向内存,否则将造成内存泄露。

4. 是否判断“传入参数与*this是否相等”,如果相等则直接返回。如果不作判断,且*this和传入参数是同一实例,那么一旦释放自身内存,则传入参数的内存也同时被释放,就再也找不到需要赋值的字符串内容了。

完整考虑上述4点,可写出如下代码:

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释放已有内容,这样做其实还存在隐患。如果在new时抛出异常,CMyString的实例将不再保持有效状态,违背了异常安全(Exception Safety)原则。可以修改如下:

CMyString& CMyString::operator = (const CMyString& str)
{
    if(this == &str)
        return *this;

    char * auxiliary = m_pData;

    m_pData = new char[strlen(str.m_pData) + 1];
    strcpy(m_pData, str.m_pData);

    delete [] auxiliary;
    auxiliary = NULL;

    return *this;
}

还有一种实现方式如下:

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

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

    return *this;
}

先创建一个临时实例strTemp,然后把strTemp.m_pData与实例自身m_pData交换。strTemp是if语句内的局部变量,会自动调用析构函数销毁,strTemp.m_pData所指向的内存就是实例之前m_pData的内存。在执行创建临时变量strTemp的构造函数时,如果new操作抛出异常,这时还没有修改实例状态,因此实例状态还是有效的,也就保证了异常安全。

【学习资料】 《剑指offer 名企面试官精讲典型面试题》

posted on 2013-01-02 13:45  zhuyf87  阅读(793)  评论(0编辑  收藏  举报

导航