class Person
{
public:
Person(const Person& p); //拷贝构造函数
Person& operator=(const Person& p); //赋值运算符重载
private:
int age;
string name;
};
拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,
拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。
这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给一个已经存在的实例。
调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。
拷贝构造函数调用时机主要有以下场景:
范例
#include
using namespace std;
//定义一个Point类
class Point{
public:
Point(int xx=0,int yy=0):x(xx),y(yy){} //构造函数
~Point(){ }; //析构函数
Point(const Point &p); //拷贝构造函数 的声明
int getX()const{return x;}
int getY()const {return y;}
private:
int x,y;//私有成员
};
//拷贝构造函数 的定义
Point::Point(const Point &p)
{
x = p.x;
y = p.y;
cout << "Calling the copy constructor" <
为了更好地理解拷贝构造函数
1、为什么要有拷贝构造函数,它跟构造函数有什么区别?
答:拷贝构造函数其实也是构造函数,只不过它的参数是const 的类自身的对象的引用。如果类里面没有指针成员(该指针成员指向动态申请的空间),是没有必要编写拷贝构造函数的 。 我们知道,如果有一个类CObj,它已经产生了一个对象ObjA,现在又用CObj去创建ObjB,如果程序中使用语句ObjB = ObjA; 也就是说直接使用ObjA的数据给ObjB赋值。这对于一般的类,没有任何问题,但是如果CObj里面有个char * pStr的成员,用来存放动态申请的字符串的地址,在ObjA中使用new 方法动态申请了内存并让ObjA.pStr指向该申请的空间,在OjbB = OjbA之后,ObjA.pStr和ObjB.pStr将同时指向那片空间,这样到导致了谁也不知道到底该由谁来负责释放那块空间,很有可能导致同一块内存被释放两次。 使用拷贝构造函数,先申请ObjA.pStr所指向的空间大小的空间,然后将空间内容拷贝过来,这样就不会同时指向同一块内存,各自有各自申请的内存,各自负责释放各自申请的内存,从而解决了刚才的问题。所以这里的“拷贝”拷贝的是动态申请的空间的内容,而不是类本身的数据。另外注意到,拷贝构造函数的参数是对象的引用,而不是对象的指针。至于为什么要用引用,不能够用指针暂时还没有搞明白,等搞明白了再说。
2、为什么要对=赋值操作符进行重载?
答:接上面的例子,用户在使用语句ObjB = ObjA的时候,或许ObjB的pStr已经指向了动态申请的空间,如果直接简单将其指向的地址覆盖,就会导致内存泄露,所以需要对=赋值操作符进行重载,在重载函数中判断pStr如果已经指向了动态申请的空间,就先将其释放。
3、拷贝构造函数和=赋值操作符重载的关系。
答:从原文的例子中可以看出,=赋值操作符重载比拷贝构造函数做得要多,它除了完成拷贝构造函数所完成的拷贝动态申请的内存的数据之外,还释放了原本自己申请的内存空间。所以原文最后给出的拷贝构造函数的实现可以使用=赋值操作符的重载来完成。
4、拷贝构造函数何时被调用?
a.对象的直接赋值也会调用拷贝构造函数 ;
b.函数参数传递只要是按值传递也调用拷贝构造函数;
c.函数返回只要是按值返回也调用拷贝构造函数。