Effective c++学习笔记——条款5:了解C++默默编写并调用哪些函数

 Know what functions C++ silently and calls
       今天继续学习一个条款,看到了本条款,让我更加了解了C++的一些内部运行机理,思考了一番,觉得C++在这方面设计真的十分严密,不亏是一个很优秀的语言,让C++一直是很受欢迎的语言。闲话少说。
       每一个class都会有一个或多个构造函数、一个析构函数、一个copy assignment操作符。这些控制着基础操作,像是产出新对象并确保它被初始化、摆脱旧对象并确保它被适当清理、以及赋予对象新值。
        那么当你当你编写了一个empty class的时候,当你利用编译器的对代码进行处理的时候,它其实已经并非是一个empty  class 了。编译器会悄悄的给你生成了 default 构造函数、一个析构函数、一个copy构造函数和一个copy assignment操作符,并且它们都是inline的。如下代码示例
  1. 一个自定类: class empty{};   

其实等于以下代码:
  1. class Empty {   
  2. public:  
  3. Empty() { }//default 构造函数   
  4.   
  5. ~Empty() { }//析构函数   
  6. Empty(const Empty& rhs) { }//copy构造函数  
  7. Empty& operator=(const Empty& rhs) { }//copy assignment操作符  
  8. };   

那么这四个名词的概念分别是:
default 构造函数:在你不提供任何构造函数的情况下,系统给出的一个不带参数,不包含函数代码的构造函数;
析构函数:与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。
copy构造函数:只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数成为构造函数(C++ pirmer定义)。经常被称作X(const X&),而且也是由编译自动调用。
copy assignment操作符:自动合成的一种赋值操作符
 
什么时候会调用copy构造函数
以下三种情况出现时,会调用一个类的拷贝构造函数:
1) 用一个已经实例化了的该类对象,去实例化该类的另外一个对象;
2) 用该类的对象传值的方式作为一个函数的参数;
3) 一个函数返回值为该类的一个对象。
如下代码进行了示例:
  1. // five.cpp : 定义控制台应用程序的入口点。   
  2.   
  3. //   
  4.   
  5.   
  6. #include "stdafx.h"   
  7.   
  8. #include <iostream>   
  9.   
  10. using namespace std;  
  11.   
  12.   
  13. class myClass  
  14.   
  15. {  
  16.   
  17.   
  18. public:  
  19.   
  20.      myClass():a(1),b(1){}  
  21.   
  22.      myClass(int m, int n)  
  23.   
  24.      {  
  25.   
  26.         a = m;  
  27.   
  28.         b = n;  
  29.   
  30. }  
  31.   
  32.     myClass(myClass& x)  
  33.   
  34.     {  
  35.   
  36.         a = x.a;  
  37.   
  38.         b = x.b;  
  39.   
  40.         cout << "copy constructor is called." << endl;  
  41.   
  42.     }  
  43.   
  44.   
  45.     friend int someFun1(myClass x);  
  46.   
  47.     friend myClass someFun2(int a, int b);  
  48.   
  49. private:  
  50.   
  51.     int a;  
  52.   
  53.     int b;  
  54.   
  55. };  
  56.   
  57.   
  58. int fun1(myClass x)  
  59.   
  60. {  
  61.   
  62.     return x.a + x.b;  
  63.   
  64. }  
  65.   
  66. myClass fun2(int a, int b)  
  67.   
  68. {  
  69.   
  70.     myClass ca(a, b);  
  71.   
  72.     return ca;   
  73.   
  74. }  
  75.   
  76. int _tmain(int argc, _TCHAR* argv[])  
  77.   
  78. {  
  79.   
  80.     myClass a;//调用已经声明的构造函数   
  81.   
  82.     myClass c(10, 10);  
  83.   
  84.     myClass d(c); // 情况1)    
  85.   
  86.     int anInt = fun1(c); // 情况2  
  87.   
  88.     myClass e = fun2(11, 11); // 情况3   
  89.   
  90.     return 0;  
  91.   
  92. }  

什么时候必须要显式声明拷贝构造函数?
拷贝构造函数的作用就是用一个已经实例化了的该类对象,去实例化该类的另外一个对象。
下面的代码并没有显式声明一个构造函数,编译器会自动为类BaseClass生成一个缺省的隐式拷贝构造函数:
  1. // five.cpp : 定义控制台应用程序的入口点。   
  2.   
  3. //   
  4.   
  5.   
  6. #include "stdafx.h"   
  7.   
  8. #include <iostream>   
  9.   
  10. using namespace std;  
  11.   
  12.   
  13. class BaseClass {  
  14.   
  15. private:  
  16.   
  17.     int a;  
  18.   
  19.   
  20. public:  
  21.   
  22.     BaseClass(int b){a = b;}  
  23.   
  24.     void SetValue(int a){this->a = a;}  
  25.   
  26.     void Show(){cout << a << endl;}  
  27.   
  28. };  
  29.   
  30.   
  31.   
  32.   
  33. int _tmain(int argc, _TCHAR* argv[]) {  
  34.   
  35.     BaseClass base(100);  
  36.   
  37.     BaseClass dev = base; // 调用了缺省的隐式拷贝构造函数  
  38.   
  39.     BaseClass bc(dev); // 调用了缺省的隐式拷贝构造函数  
  40.   
  41.   
  42.     dev.Show(); // 输出应该是100   
  43.     dev.SetValue(90);  
  44.   
  45.     dev.Show(); // 输出应该是90   
  46.     base.Show(); // 输出应该是100   
  47.     bc.Show(); // 输出应该是100   
  48.     return 0;  
  49.   
  50. }  

           注意,编译器产出的析构函数是个non-virtual(见条款7),除非这个class的base class自身声明有virtual析构函数(这种情况下这个函数的虚属性。
什么时候不会自动调用copy assignment操作符?
       至于copy构造函数和copy assignment操作符,编译器创建的版本只是单纯地将来源对象的每一个non-static成员变量拷贝到目标对象。但在某些情况下编译器拒绝生成copy assignment操作符函数。比如存在引用成员和const成员。对于引用的改变,也就是说引用自身可被改动吗?如果是,那么就违背了C++的原则:引用不能修改指向对象。所以必须自己定义copy assignment操作符。比如如下代码:
  1. // five.cpp : 定义控制台应用程序的入口点。   
  2.   
  3. //   
  4.   
  5.   
  6. #include "stdafx.h"   
  7.   
  8. #include <iostream>   
  9.   
  10. using namespace std;  
  11.   
  12.   
  13. class Person {  
  14.   
  15. public:  
  16.   
  17.     Person(string& a, const int& b)   
  18.   
  19.         : name(a), id(b) { }  
  20.   
  21. private:  
  22.   
  23.     const int id;  
  24.   
  25.     string& name;  
  26.   
  27. };  
  28.   
  29.   
  30. int _tmain(int argc, _TCHAR* argv[]) {  
  31.   
  32.     Person p1(string("chu"), 1);  
  33.   
  34.     Person p2(string("jun"), 2);  
  35.   
  36.     p1 = p2;/// error C2582: 'operator =' function is unavailable in 'Person'  
  37.   
  38.     system("pause");  
  39.   
  40.     return 0;  
  41. }   

        还有一种情况编译器不会生成copy assignment函数,就是基类型将copy assignment声明为private,派生类型就无法获得编译器的帮助。因为派生类型无法调用基类型的copy assignment函数(不具备访问权限)。
  1. <span style="font-size:13px;">#include "stdafx.h"  
  2.   
  3. #include <iostream>   
  4.   
  5. using namespace std;  
  6.   
  7.   
  8. class BaseClass {  
  9.   
  10. private:  
  11.   
  12.     BaseClass& operator=(const BaseClass& rhs) { }  
  13.   
  14. };  
  15.   
  16.   
  17. class derived : public BaseClass { };  
  18.   
  19.   
  20. int _tmain(int argc, _TCHAR* argv[]) {  
  21.   
  22.     BaseClass px1, px2;  
  23.   
  24.     px1 = px2; / 'BaseClass::operator =' : cannot access private member declared in class 'BaseClass'  
  25.   
  26.     system("pause");  
  27.   
  28.     return 0;  
  29.   
  30. }</span>  
  经过以上讲解,想必大家已经对这四个default构造函数,copy构造函数,copy assignment操作符,以及析构函数的概念已经有足够的清晰了~所以

请记住

编译器可以暗自为class创建default构造函数,copy构造函数,copy assignment操作符,以及析构函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值