我们知道,在C++中每一个类都有一个默认的构造函数,析构函数和拷贝构造函数。
对于两个对象p1,p2,实现语句p2 = p1就相当于在调用默认的拷贝构造函数,把p1对象的每个属性值逐个复制给对象p2的每个属性。所以拷贝构造函数主要用于对象之间的相互赋值。
当然,对于拷贝构造函数我们可以自定义,一般形式如下:
class_name(const class_name &object) { }
这里我们可以看出拷贝构造函数的参数是const引用类型的,用const是为了更加安全,防止对象被更改,而这里是
必须要用引用类型的,如果不用会导致无穷递归,因为将对象的实参传递给形参要调用一次复制构造,但是这次调用
同样需要一次复制构造,所以就递归了。
至于调用拷贝构造函数,我们可以分两种情况调用:显式调用和隐式调用。下面通过Point类的例子来说明。
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; class Point { private: int x,y; public: Point(int x,int y) { this->x = x; this->y = y; } //拷贝构造函数 Point(const Point &t) { x = 2 * t.x; y = 2 * t.y; } void Print() { cout<<x<<" "<<y<<endl; } }; int main() { Point p1(10,20); //Point p2(p1); //显式调用拷贝构造函数 Point p2 = p1; //隐式调用拷贝构造函数 p1.Print(); p2.Print(); return 0; }
在拷贝的过程中,实际上大体分为两类:深拷贝和浅拷贝。
对于一般的变量来说,它们之间的赋值就是浅拷贝,如上面的程序中的拷贝。而对于动态数据成员,简单的浅拷贝是不行的,因为简单的浅拷贝只是单纯的赋值。对于有动态数据成员如果进行浅拷贝,对于动态数据成员这部分,它并没有重新分配空间,而是简单的把对应的变量指向原来被拷贝的对象的动态数据成员的空间,这样就会出现一个问题,在调用析构函数时主观上会进行两次回收空间,而实际上空间只有一块。
首先我们来明确一个事实:指针之间的赋值只是存储空间多了一个别名。
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; class Point { private: int x,y; public: Point(int x,int y) { this->x = x; this->y = y; } void reSet(int x,int y) { this->x = x; this->y = y; } //拷贝构造函数 Point(const Point &t) { x = 2 * t.x; y = 2 * t.y; } void Print() { cout<<x<<" "<<y<<endl; } }; int main() { Point *p1 = new Point(40,50); Point *p2 = p1; p1->reSet(40,10); p1->Print(); p2->Print(); return 0; }
执行上面的程序,我们发现输出是:
得到这个结果是应该的,因为对于指针变量p1和p2都指向同一块内存空间。那么我们只要改变一个变量的值,那么另一个也会改变。
那么如果我们把主函数内改为:
int main() { Point p1(40,50); Point p2 = p1; p1.reSet(40,10); p1.Print(); p2.Print(); return 0; }
结果为:
这样可以看出对变量p1的修改不影响p2的值。
所以对于动态数据成员,进行深拷贝的时候,要重新分配额外的空间。如下操作:
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; class Student { private: char *name; int age; int id; public: Student(char *name,int age,int id) { this->name = new char[strlen(name) + 1]; strcpy(this->name,name); this->age = age; this->id = id; } //深拷贝 Student(const Student &t) { name = new char[strlen(t.name) + 1]; strcpy(name,t.name); age = t.age; id = t.id; } void Print() { cout<<name<<" "<<age<<" "<<id<<endl; } }; int main() { Student s1("Jack",21,1); Student s2(s1); s1.Print(); s2.Print(); return 0; }