【C++】拷贝构造函数

来源:互联网 发布:英语考四级用什么软件 编辑:程序博客网 时间:2024/04/29 04:34
在基本数据类型的初始化中,如
int a=b;
编译器只要将b的值作为a的初始值。
假设我们已定义类型Test,且有一个已经初始化的Test型对象x,如果我们希望定义一个新的Test型对象y,并且将x的值作为y的初始值,可以使用下面的语句:
Test y(x);
或者 Test y=x;
上述语句均是合法的,它只是简单地把x的成员一一复制给y,当然我们还可以重载赋值符=,这里不再详述。
由于类对象往往含有多个成员,拷贝构造函数便很重要。那么,什么时候编译器会调用拷贝构造函数?
1.用一个类对象初始化另一个类对象。
2.对象按值传递方式传入函数体。
3.对象按值传递方式从函数返回。
下面举一个使用拷贝构造函数的例子:
#include<iostream>using namespace std;class Test{public:    //为叙述方便,将访问权限设为public    int a;    int b;    Test(){}    Test(int m,int n):a(m),b(n){}    Test(const Test& t)    {        a=t.a;        b=t.b;    }};void main(){    Test x(1,2);    Test y(x);    cout<<y.a<<'\t'<<y.b<<endl;}
程序将输出1    2。
这里将x的成员一一复制给了y的成员。既然不用自己写拷贝构造函数,也可以实现这个效果,为什么还要浪费这个力气?这里我们可以“选择”性拷贝,比如只复制成员a的值,从而实现将对象初始化为特定的状态。
需要注意的是,如果在类定义中,我们没有显示地声明一个拷贝构造函数,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝,“位拷贝”又称“浅拷贝”。所以我们虽然没有声明拷贝构造函数,也可以使用Test y(x);这样的语句。
相对于“浅拷贝”,还有一种方式称为“深拷贝”,下面讨论两者的区别:
简单地说,“浅拷贝”就是就将对象的值直接复制给另一对象。然而,假设类中我们定义了一个指针成员,并且该指针已经向系统申请内存,那么通过浅拷贝的方式初始化另一对象之后,如果原对象的指针指向的内存已经被回收(如析构),我们用该对象初始化的“另一对象”的这个指针将变成野指针,从而导致程序运行出错,因为我们“浅拷贝”了原指针的值(地址),并没有分配新的内存,从而两个指针指向同一内存区域。
假设原对象已有堆资源,如果我们在拷贝的过程中分配新的堆资源给另一对象,这种方式就叫做“深拷贝”,它避免了出现野指针的问题。
下面举个深拷贝的实例:
#include<iostream>#include<string>using namespace std;class Test{public:    int num;    char* str;    Test(){}    Test(int n,char* s)    {        num=n;        str=new char[num];        strcpy(str,s);    }    Test(const Test& t)    {        num=t.num;        str=new char[num];   //深拷贝        strcpy(str,t.str);    }    ~Test(){    delete str;    }};void main(){    ……}
上述代码中的函数strcpy()为C的一个标准库函数,原型声明:extern char *strcpy(char *dest,const char *src);
头文件:string.h
功能:把从src地址开始且含有NULL结束符的字符串赋值到以dest开始的地址空间 
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 
返回指向dest的指针。

可以总结,拷贝构造函数的声明方式为:
ClassName(ClassName& ObjName); 
或ClassName(const ClassName& ObjName);
需要指出的是,必须按引用传递参数,否则在调用构造函数之前,首先要将实参拷贝给形参,这一过程又将调用拷贝构造函数,形成递归死循环,好在编译器会检查这种逻辑性,错误的声明不会编译通过。

以上实例均测试通过。

欢迎批评指正!
原创粉丝点击