函数原型
在C++中建立一个类,这个类中肯定会包括构造函数、析构函数、拷贝构造函数和重载赋值操作。
拷贝构造函数是一种特殊的构造函数,其作用也是为类的成员初始化以及为对象的构造分配存储空间。函数的名称必须和类名称一致,无返回类型,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变。
拷贝构造函数原型如下:
1 class_name(const class_name &src);
对于一个类X, 如果一个构造函数的第一个参数是下列之一:
1 & X; 2 const & X; 3 volatile & X; 4 const volatile & X;
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数,如下:
1 X::X(const & X); 2 X::X(& X, int=1); 3 X::X(& X, int a=1, int b=2);
重载赋值操作符是一个特别的赋值运算符,通常是用来把已存在的对象赋值给其它相同类型的对象。
重载赋值操作符的原型如下:
1 class_name& operator=(const class_name &src);
拷贝构造函数与重载赋值操作符的调用
当类的对象需要拷贝时,复制构造函数将会被调用。以下情况都会调用复制构造函数:
一个对象以值传递的方式传入函数体;
一个对象以值传递的方式从函数返回;
一个对象需要通过另外一个对象进行初始化。
如果对象在声明的同时将另一个已存在的对象赋给它,就会调用复制构造函数;如果对象已经存在了,然后再将另一个已存在的对象赋给它,调用的就是重载赋值运算符。
1 #include <iostream> 2 using namespace std; 3 4 class CTest 5 { 6 public: 7 CTest(){} 8 ~CTest(){} 9 10 CTest(const CTest &test) 11 { 12 cout<<"copy constructor."<<endl; 13 } 14 15 void operator=(const CTest &test) 16 { 17 cout<<"operator="<<endl; 18 } 19 20 void Test(CTest test) 21 {} 22 23 CTest Test2() 24 { 25 CTest a; 26 return a; 27 } 28 29 void Test3(CTest &test) 30 {} 31 32 CTest &Test4() 33 { 34 CTest *pA = new CTest; 35 return *pA; 36 } 37 }; 38 39 int main() 40 { 41 CTest obj; 42 43 CTest obj1(obj); // 调用复制构造函数 44 45 obj1 = obj; // 调用重载赋值操作符 46 47 /* 传参的过程中,要调用一次复制构造函数 48 * obj1入栈时会调用复制构造函数创建一个临时对象,与函数内的局部变量具有相同的作用域 49 */ 50 obj.Test(obj1); 51 52 /* 函数返回值时,调用复制构造函数;将返回值赋值给obj2时,调用重载赋值操作符 53 * 函数返回值时,也会构造一个临时对象;调用复制构造函数将返回值复制到临时对象上 54 */ 55 CTest obj2; 56 obj2 = obj.Test2(); 57 58 obj2.Test3(obj); // 参数是引用,没有调用复制构造函数 59 60 CTest obj3; 61 obj2.Test4(); // 返回值是引用,没有调用复制构造函数 62 63 return 0; 64 }
深拷贝(deep copy)与浅拷贝(shallow copy)
如果在类中没有显式地声明,那么编译器会自动生成默认的复制构造函数和重载赋值操作符。默认的复制构造函数和赋值运算符进行的都是“shallow copy”,只是简单地复制字段,把值一一赋给要拷贝的值。因此如果对象中含有动态分配的内存,就需要我们自己重写复制构造函数和重载赋值操作符来实现“deep copy”,确保数据的完整性和安全性。
例如:类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,资源重新分配,使对象拥有不同的资源,但资源的内容是一样的,这个过程就是深拷贝;反之,没有重新分配资源,两个对象就有用共同的资源,同时对资源可以访问,就是浅拷贝。浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
1 #include <iostream> 2 using namespace std; 3 class CA 4 { 5 public: 6 CA(int b,char* cstr) 7 { 8 a=b; 9 str=new char[b]; 10 strcpy(str,cstr); 11 } 12 CA(const CA& C) 13 { 14 a=C.a; 15 str=new char[a]; //深拷贝 16 if(str!=0) 17 strcpy(str,C.str); 18 } 19 void Show() 20 { 21 cout<<str<<endl; 22 } 23 ~CA() 24 { 25 delete str; 26 } 27 private: 28 int a; 29 char *str; 30 }; 31 32 int main() 33 { 34 CA A(10,"Hello!"); 35 CA B=A; 36 B.Show(); 37 return 0; 38 }
三法则(英语:rule of three,the Law of The Big Three,The Big Three;三法则,三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律,三法则的要求在于,假如类型有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类型内,亦即下列三个成员函数缺一不可:
- 析构函数(Destructor)
- 复制构造函数(copy constructor)
- 复制赋值运算符(copy assignment operator)