菜鸟笔记
提升您的技术认知

赋值运算符重载

0.对象赋值时发生了什么

C语言中允许把一个结构体赋值给另外一个相同类型的结构体,C++允许把一个对象赋值给另一个同类的对象。这是通过自动为类重载赋值运算符实现的。这种赋值运算符重载函数原型如下。

Class_name & Class_name::operator=(const Class_name &);

它接受一个指向类对象的常引用,并返回一个指向类对象的引用。c++自动提供的这个函数实现了浅拷贝。接着上篇博文的例子来验证一下。

例1

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Man {
    char* name;
    int age;
    public:
        Man(char* name, int age);
        Man();
        Man(const Man & m);
        ~Man();
        void show();
};

Man::Man(char* name, int age)
{
    cout<<"call self-def Constructor"<<endl;
    this->name = new char[strlen(name) + 1];
    strcpy(this->name, name);
    this->age = age;
}
Man::Man()
{
    cout<<"call default Constructor"<<endl;
    this->name = new char[8];
    strcpy(this->name, "Unknown");
    age = -1;
}
Man::~Man()
{
    cout<<"call Destructor"<<endl;
    delete[] name;
}
void Man::show()
{
    // cout<<"name:"<<name<<endl;
    printf("name = %s\n", name);
    cout<<"age:"<<age<<endl;
    printf("str = %p\n",name);
}
Man::Man(const Man & m)
{
    cout<<"call copy Constructor"<<endl;
    this->age = m.age;
    this->name = new char[strlen(m.name) + 1];
    strcpy(this->name, m.name);
}
int main()
{
    Man a = Man((char*)"zhengkang", 26);
    Man b;
    b = a;
    a.show();
    b.show();
    return 0;
}

这个例程的运行结果如下

从运行结果可以看出,a的name指针和b的name指针指向同一片内存区域。执行b = a;跟默认的拷贝构造函数一样,实现的是浅拷贝,那么浅拷贝存在的问题在这里同样存在。释放a的name指向的内存会导致b的name指针指向的内存也被释放掉,这就是需要重载赋值运算符的原因。

1.赋值运算符重载

进行赋值运算符重载实现深拷贝与拷贝构造函数类似,但是也有一些区别。

  1. 由于目标对象可能引用了以前分配的数据,所以函数应该使用delete[]来释放内存。
  2. 函数应该避免将对象赋值给自己,否则的话,给对象重新赋值前,释放内存操作可能删除对象的内容。
  3. 函数需要返回一个指向调用对象的引用。通过返回这样一个引用,可以实现链式操作(即连续赋值),假如a,b,c都是Man对象,那么可以这样写a=b=c;等价于a.operator=(b.operator=(c));

下面,我们重载赋值运算符函数。

例2

Man& Man::operator=(const Man& m)
{
    cout<<"call = overload func"<<endl;
    if (this == &m) //防止a = a这种情况发生
        return *this;
    delete [] name;  //先把原来指向的内存去释放掉
    age = m.age;
    name = new char[strlen(m.name) + 1];
    strcpy(name, m.name);
    return *this;
}

重新运行程序,结果如下:

两个对象的name指针指向了不同的内存区域,互不影响。

2.区分两条语句

下面两条语句可能会让人疑惑,需要区分清楚。

Man b;
b = a;		//调用复制运算符重载函数进行对象赋值
Man c = a;   //调用拷贝构造函数

例3

int main()
{
    Man a = Man((char*)"zhengkang", 26); //调用带参数的构造函数
    Man b = a; //调用拷贝构造函数
    Man c;    //调用默认构造函数
    c = a;    //调用复制运算符重载函数进行对象赋值
    return 0;
}

运行结果: