注意:该作者博客已迁移至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++提供默认的拷贝构造函数,我们可以不用写拷贝构造函数。
如果类中成员有*指针(深拷贝,浅拷贝问题),那么我们就需要写自己的拷贝构造函数。
例
若此时定义对象CExample obj1;并且obj1有指向动态分配的内存obj1.Init(40)。
现在用obj1来初始化obj2,CExample obj2=obj1,则此时调用的是默认拷贝构造函数,复制所有成员的值,所以obj1.pBuffer=obj2.pBuffer,两个对象里的指针指向了同一块内存。
如果obj1修改pBuffer指向内存里的数据,则obj2访问pBuffer指向的数据也会发生变化,这显然不是我们想要的结果。(需要让它们互不干扰)
所以我们要自己写拷贝构造函数(不是复制指针,而是复制指针指向的动态内存里的内容)。如下图:
重载赋值运算符“=”
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;)
同样,编译器为我们的类提供默认的赋值符“=”
默认”=“的恶果
- 默认的“=”只是将成员变量的值相应复制。
- 旧指针的值被丢弃了,但指针指向的内容并未释放 。
- 新指针的值被复制了,但指针所指内容并未复制 。
所以当有动态分配的内存时,要重载赋值运算符
总结
- 注意默认的拷贝构造函数和”=“的作用。
- 拷贝构造函数和赋值符的区别。
- 应该为所有包含动态分配成员的类都提供拷贝构造函数。
- 有时需要重载赋值运算符。
感谢
- 李昕老师(CUMT)