陷阱重重的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;
}
时间: 2025-01-18 15:49:13

陷阱重重的C++赋值重载函数operator=的相关文章

C++:运算符重载函数之成员运算符重载函数

5.2.3 成员运算符重载函数 在C++中可以把运算符重载函数定义为某个类的成员函数,称之为成员运算符重载函数. 1. 定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式如下: 函数类型 operator 运算符(形参表) {       函数体 } (2)成员运算符重载函数也可以在类中声明成员函数的原型,在类外定义. 在类的内部,声明成员运算符重载函数原型的格式如下: class X{      ...      函数类型 operator运算符(参数表); };

ostream类重载的operator<<()函数

ostream类重载了operator<<()以识别不同的类型,如: int short  long unsigned int unsigned short unsigned long float double     long double  char  signed char unsigned char 这样cout就不用像C语言中的printf那样需要程序员自行匹配数据类型,如:printf("%s%d%f","hello world",32,3.

C++:运算符重载函数之&quot;++&quot;、&quot;--&quot;、&quot;[ ]&quot;的应用

5.2.5 "++"和"--"的重载 对于前缀方式++ob,可以用运算符函数重载为: ob.operator++() //成员函数重载 或 operator++(X &ob) //友元函数重载,其中ob为类X的对象的引用 对于后缀方式++ob,可以用运算符函数重载为: ob.operator++(int) //成员函数重载 或 operator++(X &ob,int) //友元函数重载,其中ob为类X的对象的引用 调用时,参数int一般被传递给值0

第五篇:明确拒绝不想编译器自动生成的拷贝构造函数和赋值运算符重载函数

前言 如果你不想要编译器帮你自动生成的拷贝机制 (参考前文),那么你应当明确的拒绝. 如何拒绝?这便是本文要解决的主要问题. 问题描述 当你定义了一个类,而这个类中各对象之间也是封装的 - 禁止同类对象之间的相互赋值以及复制,那么你需要屏蔽掉编译器帮你生成的拷贝构造函数以及赋值运算符. 在许多代码中,会看到通过将拷贝构造函数和赋值运算符重载函数声明为私有且不予实现来实现这个功能.然而,这不是最科学的做法. 因为这没有做到真正的屏蔽:你在自己的成员函数中,或者友元函数中仍然可以调用这两个私有函数,

明确拒绝不想编译器自动生成的拷贝构造函数和赋值运算符重载函数

前言 如果你不想要编译器帮你自动生成的拷贝机制 (参考前文),那么你应当明确的拒绝. 如何拒绝?这便是本文要解决的主要问题. 问题描述 当你定义了一个类,而这个类中各对象之间也是封装的 - 禁止同类对象之间的相互赋值以及复制,那么你需要屏蔽掉编译器帮你生成的拷贝构造函数以及赋值运算符. 在许多代码中,会看到通过将拷贝构造函数和赋值运算符重载函数声明为私有且不予实现来实现这个功能.然而,这不是最科学的做法. 因为这没有做到真正的屏蔽:你在自己的成员函数中,或者友元函数中仍然可以调用这两个私有函数,

c++OOP之复制控制 ------复制构造函数、赋值重载、析构

本博文我们讨论OOP复制控制的一些内容: 首先考虑对象复制的时机: 非引用类型 1):根据一个类去显式或者隐式初始化一个对象: 2):复制一个对象,将它作为实参传给一个函数: 3):从函数返回时复制一个对象.(string tolittle(string word)) 一个空类,编译器提供默认无参数构造函数.拷贝构造函数.赋值运算符以及析构函数,一共四个函数.(面试) 11.复制构造函数.赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个.以String为例: a) 涉及到

拷贝构造函数与赋值运算符重载函数要点

拷贝构造函数 一个小例子 最近在<剑指Offer>上看到了一道题(程序如下),要求我们分析编译运行的结果,并提供3个选项: A. 编译错误: B. 编译成功,运行时程序崩溃:C. 编译运行正常,输出10. 1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 int value; 8 9 public: 10 A(int n) { value = n; } 11 A(A other) {

C++重载操作符operator

operator是C++关键字,用于对C++进行扩展: 1.可以被重载的操作符:new,new[],delete,delete[],+,-,*,/,%,^,&,|,~,!,=,<,>,+=,<<,>>,<<=,>>=,++,!=,<=,>=,&&,||,++,--,->*,->,(),[] 不可以被重载的操作符:.  .* :: ?: 2.基类对赋值操作符(=)重载不能被派生类继承. “+”操作符重

C++:友元运算符重载函数

运算符重载函数:实现对象之间进行算数运算,(实际上是对象的属性之间做运算),包括+(加号).-(减号).*./.=.++.--.-(负号).+(正号) 运算符重载函数分为:友元运算符重载函数.成员运算符重载函数 运算符运算符重载函数按运算类型为:双目运算符重载函数,如加.减.乘.除.赋值:   单目运算符重载函数:自加.自减.取正负号 切记:成员运算符. 和->,sezeof等不能重载.运算符重载函数的参数至少有一个是类类型或引用类型, 下面为友元运算符重载函数举例: 1 #include<i