浅析C++ 大三律

C++中几乎所有的类都有拷贝构造函数,析构函数和赋值操作符重载函数,即使你不显示定义,编译器也会自动生成的,它们提供的都是一些最基本的功能

拷贝构造函数一种特殊的构造函数,他由编译器调用来完成一些基于同一类的其他对象的构件及初始化;

析构函数摧毁一个对象并保证它被彻底清除;

赋值操作符:以已有对象为蓝本另一对象进行新的值。

所谓的大三律(rule of three, the law of the big three or the big three)正是在规则他们之间的关系:

1.如果类定义了析构函数,那么也应该定义拷贝构造和赋值运算符;

2.如果类含有需要动态分配的成员,那么该类必须定义拷贝构造和赋值运算符;

一句话,析构函数、拷贝构造、赋值运算符重载应该总是同时出现。下面一个简单的例子程序对这个定律做了论证:

 

 1 #include  < iostream >
 2
 3 using   namespace  std;
 4
 5 class  Test
 6 {
 7  public:
 8    Test() 
 9    {  
10      _pdata = new char[100]; 
11    }
;
12
13    ~Test() 
14    
15      delete [] _pdata; 
16    }
;
17  
18  private:
19    char* _pdata;
20}
;
21
22
23 int  main()
24
25  Test a;
26  Test b;
27  
28  b = a;
29
30  return 0;
31}

32
上面这么一段简单的代码看似没什么问题,然而却隐藏着严重的内存泄露。
在25行定义了一个类的对象,同时对象a中的数据成员_pdata也在堆中分配了内存,同理26行对对象b做了同样的事情,然而28行处将a赋值给b,默认赋值运算符做的是对数据成员逐个的赋值,或者说是做了浅拷贝,所以对象b中的成员_pdata指向了跟对象a中的_pdata的同一块内存,在析构的时候就会产生a中的_pdata指向的那块内存被释放了两次,而原先对象b中分配的内存却没有释放的问题。
如果将26、28行换成Test b = a或Test b(a),结果是一样的,不同的是调用的是拷贝构造函数。

既然知道了问题所在,那么解决的方法当然自己来定义拷贝构造和赋值运算符了,具体做法就不列举了。

在某些情况下,实现类的拷贝构造函数和赋值操作符是非常麻烦的时候,特别是确定程序中不会做拷贝和赋值操作的时候,去实现它们确实有点得不偿失。而不定义又怕出现上述问题,在这里有个巧妙(与其说巧妙不如说偷懒:-))的方法,就是可以只将它们声明为private成员而不去实现它们。这样既可以防止了会有人去显示调用它们,也防止了编译器自动生成。

顺便提一下拷贝构造被调用的三种场合:
1.在声明语句中用一个对象初始化另一个对象;
2.将一个对象作为参数按值调用方式传递给另一个对象时生成对象副本;
3.生成一个临时对象作为函数的返回结果;

原文地址: http://www.cppblog.com/wolf/articles/68785.html

你可能感兴趣的:(浅析C++ 大三律)