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

注意:该作者博客已迁移至https://buxianshan.xyz

拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数是拷贝构造函数。(C++ premier里的定义)

拷贝构造函数应用的场景:

  • 用一个对象初始化另外一个对象
  • 函数的参数是一个对象,并且是值传递方式
  • 函数的返回值是一个对象,并且是值传递方式

特别注意的是定义一个类时,编译器会给我们定义一个默认拷贝构造函数(点击查看默认成员函数)

默认拷贝构造函数

class CTest
{
public:
 int i;
 CTest(){cout << "construct" << endl;}
 ~CTest(){cout << "discontruct" << endl;}
};
void test(CTest obj)
{
}
int main()
{
 CTest testObj;
 test(testObj);
 return 0;
}

这个程序运行的结果为:

construct
discontruct
discontruct

当 testObj 以值传递的方式传入test函数时,此时会生成一个CTest类型的临时变量,但是此时编译器采用的是位拷贝(按字节复制)的方式,调用默认拷贝构造函数,并不调用 CTest类的构造函数。

对于类中普通的成员变量,如int, double, char等,c++提供默认的拷贝构造函数,我们可以不用写拷贝构造函数。

如果类中成员有*指针(深拷贝,浅拷贝问题),那么我们就需要写自己的拷贝构造函数。


拷贝构造函数与重载赋值运算符_第1张图片
若此时定义对象CExample obj1;并且obj1有指向动态分配的内存obj1.Init(40)。
现在用obj1来初始化obj2,CExample obj2=obj1,则此时调用的是默认拷贝构造函数,复制所有成员的值,所以obj1.pBuffer=obj2.pBuffer,两个对象里的指针指向了同一块内存。
如果obj1修改pBuffer指向内存里的数据,则obj2访问pBuffer指向的数据也会发生变化,这显然不是我们想要的结果。(需要让它们互不干扰)

所以我们要自己写拷贝构造函数(不是复制指针,而是复制指针指向的动态内存里的内容)。如下图:
拷贝构造函数与重载赋值运算符_第2张图片

重载赋值运算符“=”

CTest test1;
CTest test2 = test1;
CTest test2 (test1); 

此时调用test1的拷贝构造函数初始化对象test2

但是对于下面的表达式:调用的是重载的赋值运算符

CTest test1,test2;
test2 = test1;

拷贝构造函数和赋值的区别

  • 用一个已存在的对象去构造一个不存在的对象(构造之前不存在),就是拷贝构造.
  • 用一个已存在的对象去覆盖另一个已存在的对象,就是赋值运算.
  • 拷贝构造函数从一个已经存在的变量来初始化一个新声明的变量,不需要清除现有的值(因为是新创建,所以没有现有值)
  • 拷贝构造函数没有返回值。
  • 赋值运算符return *this.(This is necessary to allow multiple assignment, eg x = y = z;)

同样,编译器为我们的类提供默认的赋值符“=”
拷贝构造函数与重载赋值运算符_第3张图片
默认”=“的恶果

  • 默认的“=”只是将成员变量的值相应复制。
  • 旧指针的值被丢弃了,但指针指向的内容并未释放 。
  • 新指针的值被复制了,但指针所指内容并未复制 。

所以当有动态分配的内存时,要重载赋值运算符
拷贝构造函数与重载赋值运算符_第4张图片

总结

  • 注意默认的拷贝构造函数和”=“的作用。
  • 拷贝构造函数和赋值符的区别。
  • 应该为所有包含动态分配成员的类都提供拷贝构造函数。
  • 有时需要重载赋值运算符。

感谢

  • 李昕老师(CUMT)

你可能感兴趣的:(C/C++)