拷贝构造函数:用一个已有的对象来初始化一个被创建的同类的对象,是一种特殊的构造函数。
浅拷贝和深拷贝的区别是什么呢?
下面是一个Test类,类中包含一个私有的指针p, 现在我想创建两个对象,其中一个对象通过复制另一个对象创建,于是编写了这样的代码:
class Test{
private:
char *p;
public:
Test(const char *str){
p=new char[strlen(str)+1];
strcpy(p,str);
printf("Test cntr.%s\n",p);
}
~Test(){
printf("~Test cntr.%s\n",p);
delete []p;
p=NULL;
}
};
int main(){
Test t1("hello");
Test t2(t1);
}
运行结果是这样的:
很明显,创建失败了。。。从结果上可以看到程序只调用了一次程序中带参数的构造函数,却析构了两次,而且第一个对象的析构结果啥玩意儿?(先构造后析构的原则)原来如果我们没有主动编写类的拷贝构造函数,系统就会自动生成一个缺省拷贝构造函数。而缺省拷贝构造函数在拷贝过程中是位复制,把一个对象按位复制过来,对于指针型的成员变量只复制指针本身,而不复制指针所指向的内容。
t1.p 和 t2.p指向的是同一块地址,所以构造函数只调用了一次,析构时先释放了 t2.p 指向的内存,再析构 t1.p 时则要访问同一个已经被释放了的内存单元,这时则出现了内存管理错误!
那怎么样既复制指针本身,也复制指针内容呢?不要缺省拷贝构造函数,我们再编写个能实现深拷贝的构造函数,为t2新开辟一个内存单元
我加了个拷贝构造函数
Test(const Test &t)
{
p=new char[strlen(t.p+1)];
strcpy(p,t.p);
printf("Test copy.%s\n",p);
}
运行结果:
复制成功*.*
解释一下 Test(const Test &t) 这里为什么是引用。 如果是Test(const Test t) ,这时实参到形参的传递是值传递,也就是实参复制一份给形参。可是在执行Test t2(t1)调用这个拷贝构造函数时,还没有复制t1, 为了避免出现 未复制–>已复制–>再复制 的乌龙情况,则需要引用。
除了使用重载拷贝构造函数的方法,还可以通过重载赋值运算符的方法(赋值函数)实现深拷贝。不过要注意拷贝构造函数与赋值函数的区别:
class Test{
private:
char *p;
public:
Test(const char *str){
p=new char[strlen(str)+1];
strcpy(p,str);
printf("Test cntr.%s\n",p);
}
Test(const Test &t)
{
p=new char[strlen(t.p+1)];
strcpy(p,t.p);
printf("Test copy.%s\n",p);
}
Test &operator=(const Test &t){
if(this!=&t){
delete [] p;//赋值函数中要先删除对象自己本身的指针空间
p=new char[strlen(t.p+1)];
strcpy(p,t.p);
}
printf("Test == opertor.%s\n",p);
return *this;
}
~Test(){
printf("~Test cntr.%s\n",p);
delete []p;
p=NULL;
}
};
int main(){
Test t1("hello");
Test t2(t1);
Test t3="world";
Test t4(t3);
t4=t1;//赋值函数
}
运行结果:
t4=t1调用的是赋值函数,注意到区别了吗?
在创建对象的时候调用拷贝构造函数,对象已存在再复制时调用的是赋值函数。 Test t1(“hello”); Test t2=t1; 这样也是调用拷贝构造函数,但为了形式上区分赋值函数,将Test t2=t1;写成Test t2(t1);
其次赋值的本质是删除原值,赋予新值,所以赋值函数中要先删除对象自己本身的指针空间。
重点
浅拷贝:
缺省拷贝构造函数在拷贝过程中是位复制,把一个对象按位复制过来,对于指针型的成员变量只复制指针本身,而不复制指针所指向的内容。
深拷贝:由于对象的复制默认都是浅拷贝,如果我们想要的不仅复制指针本身,也要复制指针内容,则需要重载拷贝构造函数或者编写赋值函数(注意两者区别),使之成为深拷贝。
最近在整理一些知识点,如有错误之处,希望大家多多指正呀!