一,拷贝构造函数
拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(const X& x)。自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。
对于普通类型的对象,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
#include <QCoreApplication>
#include <iostream>
using namespace std;
class Test{
private:
int a;
public:
Test(int b){
a=b;
}
void excute(){
cout<<a<<endl;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test A(100);
Test B=A;
B.excute();
return a.exec();
}
运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。下面举例说明拷贝构造函数的工作过程。
#include <QCoreApplication>
#include <iostream>
using namespace std;
class Test{
private:
int a;
public:
Test(int b){
a=b;
}
Test(const Test&C){//拷贝构造函数
a=C.a;
}
void excute(){
cout<<a<<endl;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test A(100);
Test B=A;
B.excute();
return a.exec();
}
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
- 当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数
BOOL testfunc(CExample obj); testfunc(theObjone); //对象直接作为参数。 BOOL testfunc(CExample obj) { //针对obj的操作实际上是针对复制后的临时拷贝进行的 }
- 当函数中的局部对象作为返回值被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用。
CTest func()
{
CTest theTest;
return theTest
}
- 一个对象需要通过另外一个对象进行初始化。
CExample A(100); CExample B = A;
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝。
二,赋值构造函数
iint main(int argc, char * argv[])
{
CExample A;
A.Init(40);
CExample C;
C.Init(60);
//现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。
C = A;
return 0;
}
用到了"="号,但与上面的例子中语句“ CExample B=A; ” 不同“ CExample B=A; ”语句中的 "=" 在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号表示。 例CExample B(A);
而本例子中,"=" 表示赋值操作。将对象 A 的内容复制到对象C;,这其中涉及到对象C 原有内容的丢弃,新内容的复制。 但"="的缺省操作只是将成员变量的值相应复制。旧的值被自然丢弃。 由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放。指针的值被复制了,但指针所指内容并未复制。 因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值操作符号。
注意:拷贝构造函数是在对象被创建时调用的,而赋值构造函数只能被已经存在了的对象调用
String a(“hello”);
String b(“world”);
String c = a; // 调用了拷贝构造函数,最好写成 c(a);
c = b; // 调用了赋值构造函数
三,浅拷贝的问题
浅拷贝也叫位拷贝,拷贝的是地址。
深拷贝也叫值拷贝, 拷贝的是内容。
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。
#include <QCoreApplication>
#include <iostream>
using namespace std;
class Test{
private:
int a;
char *str;
public:
Test(int b,const char* _str){//构造函数
a=b;
str=new char[b];
strcpy(str,_str);
}
Test(const Test&C){//拷贝构造函数
a=C.a;
str = new char[a];//深拷贝
if(str!=0)
strcpy(str,C.str);
}
Test& operator =(const Test& D){//赋值构造函数
a=D.a;//复制常规成员
char *temp = new char[a];//复制指针指向的内容
strcpy(temp,D.str);
delete []str;//删除原指针内容
str=temp;//建立新的指向
return *this;
}
void excute(){
cout<<str<<endl;
}
~Test(){//析构函数
delete str;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//拷贝构造函数测试
Test A(20,"hello!");
Test B=A;
B.excute();
//赋值构造函数测试
Test C(30,"I love you!");
Test D(30,"I hate you!");
C=D;
C.excute();
return a.exec();
}