今天在上课时听王石锟说起“复制构造函数”这个概念。
如果一个类包含有指向另一个对象的指针数据成员,应为该类提供复制构造函数,通常还要提供一个重载的赋值运算符,以确保对象正确赋值。
为什么要这样做呢,如果一个类中包含有另一个对象的指针数据成员,譬如参考E:/vc_work/Idea_test/Part3_2/,这个指针指向一个结构对象(动态分配内存的)。当类的两个对象Inshdl,Ins2之间赋值后,两个对象的指针成员是指向同一块内存的,这块内存是由创建它的对象Inshdl分配的。在后面的操作中,任何一个对象:Inshdl或Ins2结束自己对象生命时(delete操作),都能销毁这个指针指向的内存。这时,另外一个对象就无法访问这块内存了,从而造成了错误。
看看下面的参考文章:
1、 就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的,在上面的代码中,我们并没有看到拷贝构造函数,同样完成了复制工作,这又是为什么呢?
因为当一个类没有自定义的拷贝构造函数的时候系统会自动提供一个默认的拷贝构造函数,来完成复制工作。
2、复制构造函数(也就是copy构造函数)的格式:Test(Test &c_t) 其中,Test是类名。注意参数是该类对象的引用
3、当用一个已经初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候系统将会提供给一个默认的拷贝构造函数来完成这个过程。默认复制构造函数采用的是“浅拷贝”的形式,也就是说,直接将各个成员变量相应赋值。
4、在某写状况下,类体内的成员是需要开辟动态开辟堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱,试想一下,已经开辟的一端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已经开辟的堆地址,一旦程序产生析构,释放堆的时候,计算机是不可能清楚这段地址是真正属于谁的,当连续发生两次析构的时候就出现了运行错误。(错误正是发生在这时候)
5、看下面这个例子:
#include <iostream>
using namespace std;
class Internet
{
public:
Internet(char *name,char *address)
{
cout<<"载入构造函数"<<endl;
strcpy(Internet::name,name);
strcpy(Internet::address,address);
cname=new char[strlen(name)+1];
if(cname!=NULL)
{
strcpy(Internet::cname,name);
}
}
Internet(Internet &temp)
{
cout<<"载入COPY构造函数"<<endl;
strcpy(Internet::name,temp.name);
strcpy(Internet::address,temp.address);
cname=new char[strlen(name)+1];//这里注意,深拷贝的体现!
if(cname!=NULL)
{
strcpy(Internet::cname,name);
}
}
~Internet()
{
cout<<"载入析构函数!";
delete[] cname;
cin.get();
}
void show();
protected:
char name[20];
char address[30];
char *cname;
};
void Internet::show()
{
cout<<name<<":"<<address<<cname<<endl;
}
void test(Internet ts)
{
cout<<"载入test函数"<<endl;
}