陷阱重重的C++赋值重载函数operator=

曾经有C++高手说过:看一个C++程序员功底是否够硬,让他写个赋值重载函数就能看出来了!在我看来,这种说法并不夸张。因为能将operator=函数写好确实需要扎实的基础,其中的陷阱真不少。

  • 陷阱一:不懂规避自我拷贝

先看代码

string& string::operator=(const string& rhs)
{
    if (m_pStr != NULL)
        delete [] m_pStr;
    m_pStr = new char[....];
    strcpy(....);

    return *this;
}

此代码就是没有规避自我赋值,那么如果进行以下的调用,那么后果很严重。

string myStr("abc");
myStr = myStr;

赋值操作中,会先把自己的数据delete掉,然后再strcpy一个空值,程序立马挂了。

所以,在赋值函数开始的时候,需要防止自我复制。写法如下:

string& string::operator=(const string& rhs)
{
    // 防止自我赋值
    if (this == &rhs)
        return * this;

    ...
}
但有些书籍上面使用以下写法,不敢苟同。

string& string::operator=(const string& rhs)
{
    // 防止自我赋值
    if (*this == rhs) // 这只是判断内容是否相同,并不能判定是自我赋值!!!
        return * this;

    ...
}
  • 陷阱二:返回值的类型用啥

在初学者的群体当中,有出现诸如以下几种返回值版本:

// 版本一
string string::operator=(const string& rhs)
{
    ...
    return * this;
}

// 版本二
const string& string::operator=(const string& rhs)
{
    ...
    return * this;
}

// 版本三
string* string::operator=(const string& rhs)
{
    ...
    return this;
}

版本一的缺陷:多了一个临时对象的生成和拷贝,影响程序效率。

版本二的缺陷:非const类型的变量想得到它的连续赋值变得不可能,而且编译器也会报错。

版本三的缺陷:不能保持连续赋值时类型的统一性,违反了编程的惯例。如

// 版本三的缺陷
string a, b, c;
*a = b = c; // 必须这样赋值给a,类型不统一!! 
  • 陷阱三:未做深拷贝

任何未做深拷贝的版本都退化为默认版本。

string& string::operator=(const string& rhs)
{
    if (this == &rhs) return *this;

    if (m_pStr != NULL)
        delete [] m_pStr;
    m_pStr = rhs.m_pStr; // 浅拷贝

    return *this;
}


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。