拷贝构造函数:对于对象的复制时使用的一种特殊的函数。
以下三种情况是对对象的复制:
- 建立一个新的对象并用已有对象对新对象初始化:
1 class Text 2 { 3 int a; 4 int b; 5 }; 6 7 main() 8 { 9 Text text1; 10 Text text2(text1); 11 return 0; 12 }
- 作为函数参数传递:
1 void fun(Text text) 2 { 3 4 } 5 6 main() 7 { 8 Text text1; 9 Text fun(text1); 10 return 0; 11 }
- 函数返回值是类的对象:
1 Text fun( ) 2 { 3 Text text; 4 return text; 5 } 6 7 main() 8 { 9 Text text1; 10 text1 = fun(); 11 return 0; 12 }
缺省的拷贝构造函数:
- 如果类中没有说明拷贝构造函数,编译器会自动生成一个缺省的拷贝构造函数,作为该类的公有成员,来进行对象间的位拷贝,这个拷贝构造函数简单的关联了所有的类成员
- 每个类必须有一个拷贝构造函数
定义形式:
<类名>::<类名>(const <类名>&<引用名>)
例子:
1 #include <iostream> 2 3 using namespace std; 4 5 class Line 6 { 7 public: 8 int getLength( void ); 9 Line( int len ); // 简单的构造函数 10 Line( const Line &obj); // 拷贝构造函数 11 ~Line(); // 析构函数 12 13 private: 14 int *ptr; 15 }; 16 17 // 成员函数定义,包括构造函数 18 Line::Line(int len) 19 { 20 cout << "调用构造函数" << endl; 21 // 为指针分配内存 22 ptr = new int; 23 *ptr = len; 24 } 25 26 Line::Line(const Line &obj) 27 { 28 cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl; 29 ptr = new int; 30 *ptr = *obj.ptr; // 拷贝值 31 } 32 33 Line::~Line(void) 34 { 35 cout << "释放内存" << endl; 36 delete ptr; 37 } 38 int Line::getLength( void ) 39 { 40 return *ptr; 41 } 42 43 void display(Line obj) 44 { 45 cout << "line 大小 : " << obj.getLength() <<endl; 46 } 47 48 // 程序的主函数 49 int main( ) 50 { 51 Line line(10); 52 53 display(line); 54 55 return 0; 56 }
如果在后两种情况不使用拷贝构造函数的时候,就会导致进行两次析构函数一个指针指向已经被删除的内存空间。对于第一种情况来说,初始化和赋值的不同含义是拷贝构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
使用原则
通常的原则是:
①对于凡是包含动态分配成员或包含指针和引用成员的类都应该提供拷贝构造函数;
②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。
深拷贝和浅拷贝:
编译器默认的方式是浅拷贝,浅拷贝就是对对象的简单赋值,一般情况浅拷贝就可以了,但是如果在堆上new一个新的空间的话对象就存在动态成员,浅拷贝就会出错。
1 #include <iostream> 2 3 using namespace std; 4 5 class Rect 6 { 7 public: 8 Rect() 9 { 10 cout << "rect" << endl; 11 p = new int(100); 12 } 13 14 ~Rect() 15 { 16 cout << "~rect" << endl; 17 if (p != NULL) 18 { 19 delete p; 20 } 21 } 22 private: 23 int width; 24 int height; 25 int *p; 26 }; 27 28 int main() 29 { 30 Rect rect1; 31 Rect rect2(rect1); 32 return 0; 33 }
错误分析:
代码会运行1次构造函数,两次析构函数,这样第二次的析构函数释放的空间不存在。
深拷贝:
深拷贝会在堆上重新分配一块空间,这样就不会出现上面那种问题了。
1 #include <iostream> 2 3 using namespace std; 4 5 class Rect 6 { 7 public: 8 Rect() 9 { 10 cout << "rect" << endl; 11 p = new int(100); 12 } 13 Rect(const Rect& r) 14 { 15 width = r.width; 16 height = r.height; 17 p = new int;// 为对象重新分配空间 18 *p = *(r.p); 19 } 20 ~Rect() 21 { 22 cout << "~rect" << endl; 23 if (p != NULL) 24 { 25 delete p; 26 } 27 } 28 private: 29 int width; 30 int height; 31 int *p; 32 }; 33 34 int main() 35 { 36 Rect rect1; 37 Rect rect2(rect1); 38 return 0; 39 }