拷贝构造函数
每个类都有多个构造函数和一个析构函数。此外,类还可以有一个拷贝构造函数(copy constructor)。
拷贝构造函数用于拷贝对象。也就是说,这个构造函数的参数是这个类的对象,执行这个构造函数之后,会产生一个与原来对象一样的对象(实际上是用原来对象的数据初始化了一个对象)。
构造函数的签名如下:
ClassName (const ClassName&)
下面给出一个栗子:
Circle.h
class Circle { public: //无参构造函数 Circle(); //有参构造函数 Circle(double); //析构函数 ~Circle(); //获取圆的面积 double getArea(); //获取圆的半径 double getRadius(); private: //圆的半径 double radius; //定义常量圆周率 const double PI = 3.14159; //对象个数 static int objectNumber; };
Circle.cpp
#include "Circle.h" #include <iostream> using namespace std; int Circle::objectNumber = 0; //无参构造函数 Circle::Circle() { this->radius = 1; objectNumber++; cout << "当前对象的个数是" << objectNumber << endl; } //有参构造函数 Circle::Circle(double radius) { this->radius = radius; objectNumber++; cout << "当前对象的个数是" << objectNumber << endl; } //析构函数 Circle::~Circle(){ objectNumber--; cout << "当前对象的个数是" << objectNumber << endl; } //获取面积 double Circle::getArea() { return radius*radius*PI; } //获取半径 double Circle::getRadius(){ return this->radius; }
main.cpp
#include <iostream> #include "Circle.h" using namespace std; int main() { //调用无参构造函数 cout << "创建对象p1" << endl; Circle* p1 = new Circle(2); //调用拷贝构造函数 cout << "创建对象p2" << endl; Circle* p2 = new Circle(*p1); //调用有参构造函数,参数为3 cout << "创建对象p3" << endl; Circle* p3 = new Circle(3); cout << "-------------------------------"<< endl; cout << "p1半径为" << p1->getRadius() << endl; cout << "p2半径为" << p2->getRadius() << endl; cout << "p3半径为" << p3->getRadius() << endl; cout << "-------------------------------"<< endl; //销毁p1 cout << "销毁对象p1" << endl; delete p1; //销毁p2 cout << "销毁对象p2" << endl; delete p2; //销毁p3 cout << "销毁对象p3" << endl; delete p3; return 0; }
运行结果:
由于我们调用的是缺省的拷贝构造函数,所以创建p2时,对象的个数没有加1。
还有一个问题是,我们调用的拷贝构造函数,是将p1的地址给了p2,还是新开辟了内存?
更改一下main.cpp
#include <iostream> #include "Circle.h" using namespace std; int main() { //调用无参构造函数 cout << "创建对象p1" << endl; Circle p1(2); //调用拷贝构造函数 cout << "创建对象p2" << endl; Circle p2(p1); //调用有参构造函数,参数为3 cout << "创建对象p3" << endl; Circle p3(3); cout << "-------------------------------"<< endl; cout << "p1半径为" << p1.getRadius() << endl; cout << "p1地址为" << &p1 << endl; //销毁p1 cout << "销毁对象p1" << endl; delete &p1; cout << "-------------------------------"<< endl; cout << "p2半径为" << p2.getRadius() << endl; cout << "p2地址为" << &p2 << endl; //销毁p2 cout << "销毁对象p2" << endl; delete &p2; cout << "-------------------------------"<< endl; cout << "p3半径为" << p3.getRadius() << endl; //销毁p3 cout << "销毁对象p3" << endl; delete &p3; return 0; }
运行结果:
可以看到,p1和p2有着两个不同的地址,而且p1被delete之后,p2的值还在。所以,拷贝构造函数会新开辟内存空间。
自定义拷贝构造函数
接下来解决之前对象个数不一致的问题。为了能让我们的对象个数在拷贝构造时也变化,我们就要自定义一个拷贝构造函数。
首先在Circle.h的public下添加:
//拷贝构造函数 Circle (const Circle&);
然后在Circle.cpp中添加函数体:
//拷贝构造函数 Circle::Circle(const Circle& c) { this->radius = c.radius; objectNumber++; cout << "拷贝成功" << endl; cout << "当前对象的个数是" << objectNumber << endl; }
然后再次运行main.cpp(main.cpp不变)
OK,现在就符合实际情况了。
深拷贝和浅拷贝
之前我们说到的都是浅拷贝。也就是说,如果类中有一个指针变量,在拷贝时,指针变量的值不会改变。这时,两个虽然两个对象具有不同的地址,但是两个对象的指针却指向同一个地址。当一个对象被销毁时,它的指针指向的内存也会销毁。因此,另一个对象销毁时,就会产生错误。
由此引出深拷贝。即让指针不指向同一个地址,这时就一定要自定义拷贝构造函数,给新对象的指针分配内存了。
(顺便一提,浅拷贝构造函数相当于用=,也就是说Circle p2(p1)也可以写为Circle p2 = p1)
假设我的类中有一个指针p,下面是浅拷贝构造函数:
//浅拷贝构造函数 Circle::Circle(const Circle& c) { this->radius = c.radius; this->p = c.p; objectNumber++; cout << "拷贝成功" << endl; cout << "当前对象的个数是" << objectNumber << endl; }
此时,没有为新对象的指针分配内存
main.cpp如下:
#include <iostream> #include "Circle.h" using namespace std; int main() { //调用无参构造函数 cout << "创建对象p1" << endl; Circle p1(2); //调用有参构造函数,参数为1 cout << "创建对象p2" << endl; Circle p2(p1); cout << "-------------------------------"<< endl; cout << "p1的指针p的值为" << p1.p << endl; cout << "-------------------------------"<< endl; cout << "p2的指针p的值为" << p2.p << endl; return 0; }
运行结果:
可以看到,两个指针的值都是0x82a978
再看深拷贝的拷贝构造函数:
//深拷贝构造函数 Circle::Circle(const Circle& c) { this->radius = c.radius; //先开辟内存 this->p = new int; //再赋值 *p = *(c.p); objectNumber++; cout << "拷贝成功" << endl; cout << "当前对象的个数是" << objectNumber << endl; }
main.cpp同上
运行结果:
此时两个指针指向的就是不同的地址了。