23、【C++基础】拷贝构造函数和重载赋值运算符

函数原型

在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)
原文地址:https://www.cnblogs.com/Long-w/p/9675893.html