• C++ 构造函数和析构函数(Constructors & Destructors)


    一、定义:

      当object产生,有一个特殊的称为constructor的函数会自动执行。当object死亡,有一个特殊的称为destructor的函数会自动执行。Constructor 可以不只一个, destructor 只能有一个。

      Constructor(构造函数)就是与class同名的那些member functions,例如CPoint::CPoint()或CDemo::CDemo()。Constructors不能够指定返回值类型,也就是它不必(但可以)return。constructions可以有一个或多个,各有不同类型的参数。

      Destructor(析构函数)就是与class同名,且前面有加“~”符号的那个member function,例如例如 CPoint::~CPoint()  CDemo::~CDemo()Destructor 不能指定返回值类型,也就是它不必(但可以)return。每个class只有一个destructor,并且不能有任何参数。

      由于global object的诞生比程序进入更早点,所以global object的constructor执行的时间更早于程序的进入点。

    二、Default Constructors

      所谓的default constructor就是没有指定任何的参数的constructor。如果我们的class CA声明如下:

    复制代码
     1 #include 
     2 using namespace std;
     3 class CA
     4 {
     5  public:
     6     int getdata(){return _data;};
     7     CA(){_data =5;};
     8     CA(int val){_data =val;};
     9     
    10  protected:
    11     int _data; 
    12     
    13 };
    复制代码

      CA有两个constructors,分别是CA(int)  CA()。后都没有参数,正是所谓的default constructor。当我产生一个 CA object 而沒有指定任何参数:

    1 CA aCA;
    2 CA* pCA = new CA;

     

      编译器就为我们呼叫default constructor

      普遍存在于C++程序员之间的一个误解是:如果我们没有为某个class设计constructor,编译器会自动为该class制造出一个default constructor来。这个说法也对也不对,这里我先提示一个结论,销稍后有更多细节探讨。什么是编译器所需要的动作?就是隐藏在C++程序代码下面,让C++诸多特征得到实现的动作,包含以下三种情况:

     

      1. class CA class CZ objects,如图1:当产生一个CA object 时,隐藏在底层的必要动作是先初始化CZ object(因为CA object中有一个CZ object)

      2.class CA继承于class CZ,如图2:当产生一个CA object 时,隐藏在底层的必要动作是先调用CZ的default constructor (因为CA object中有一个CZ subobject)

      3.CA是一个 polymorphic class,也就是说它有virtual functions,或继承于有virtual functions的class。当产生一个CA object,隐藏在底层的必要动作是:将虚拟机制所需要的vptr和vtb1初始化。

      由于这三种情况皆有所谓的“底层的必要动作”,所以编译器必须自动为class CA生成一个default constructor((如果沒有任何 user-defined constructor 的话),或暗中对已有的user-defined constructor 动作脚(添加一些代码)。生成出来的东西称为“implicit nontrivial default constructor”。

      如果沒有上述需求,编译器就不会为class产生一个default constructor,例如: 

    复制代码
    1 class CB
    2 { public:
    3 int getdata() { return _data; };
    4 protected:
    5 int _data;
    6 };
    复制代码

     

    virtual functions的base classes),也没有内含embedded objects,也没有继承于其它class,所以编译器不会为它生成一个default constructor出来,于是当我们这么做:

    1 CB aCB; // 应该调用default constructor
    2 cout << "aCB.getdata()=" << aCB.getdata() << endl;

      但得到的结果如下:

    aCB.getdata()=4211382 // 莫名其妙的初值

      这个恐怕不是我所期望的,我们期望的_data有个初始值,但这个不是编译器的需求,所以我们只能自求多福,自已设计一个default constructor:

    复制代码
    1 class CB
    2 { public:
    3     int getdata() { return _data; };
    4     CB() { _data = 5; }; // default constructor
    5 protected:
    6     int _data;
    7 };
    复制代码

      现在输出的结果就是我们所期望的了:

    CB aCB; // 应该调用  default constructor
    cout << "aCB.getdata()=" << aCB.getdata() << endl;
    // 输出结果:aCB.getdata()=5

      注意,如果class已经有了任何constructor,但不是default constructor,编译器绝不会为它生成一个default constructor。如果class CZ正是如此一个class,那么当你想产生一个CZ object,并且没有指定参数时:

    CZ *aCZ; // error

      编译器会输出异常提示:

    error C2512: 'CZ' : no appropriate default constructor available

    三、Copy Constructors

      所谓的Copy Constructors是指有一个参数的类型是其 class type constructor,例如:

    复制代码
     1 class CA
     2 {
     3 public:
     4     int getdata() {return _data; };     //default constructor
     5     CA(){_data =5;};
     6     CA(int val){_data=val; };           //constructor
     7     CA(const CA& ca){                  //copy constructor
     8         cout<<"copy constructor"<10 
    11     };
    12 
    13 protected:
    14     int _data;
    15 };
    复制代码

       以下两种情况,会唤起copy constructor:

      情况1:将一个object当做参数传给某一个函数;

      情况2:将一个object当做函数的返回值;

      例如(沿用上面的class CA):

    复制代码
     1 #include 
     2 
     3 using namespace std;
     4 class CA
     5 {
     6 public:
     7     int getdata() {return _data; };     //default constructor
     8     CA(){_data =5;};
     9     CA(int val){_data=val; };           //constructor
    10     CA(const CA& ca){                  //copy constructor
    11         cout<<"copy constructor"<<endl;
    12         _data=10;
    13 
    14     };
    15 
    16 protected:
    17     int _data;
    18 };
    19 
    20 void foo2(CA aCA)//情况1(函数参数是个object)
    21 {
    22     cout<<"in foo2(),aCA.getdata()="<endl;
    23 }
    24 
    25 CA foo3()//情况2(函数的返回值是个object)
    26 {
    27     CA aCA(3);
    28     cout<<"in foo3(),aCA.getdata()="<endl;
    29     return aCA;
    30 }
    31 
    32 int main()
    33 {
    34 
    35     CA aCA1,aCA2(7);
    36     cout << "aCA1.getdata()="< endl;
    37     cout << "aCA2.getdata()="< endl;
    38     aCA2 =aCA1;//object assignment
    39     cout << "aCA2.getdata()="< endl;
    40     foo2(aCA1);//情况1(调用之前aCA1._data 为5)
    41     aCA2=foo3();
    42     cout << "aCA2.getdata()="< endl;
    43 
    44     return 0;
    45 }
    复制代码

      让我们看看执行结果:

    复制代码
    aCA1.getdata()=5
    aCA2.getdata()=7
    aCA2.getdata()=5 // 经过 object assignment 之后
    copy constructor
    in foo2(), aCA.getdata()=10 // 经过情況 1 之后
    in foo3(), aCA.getdata()=3
    copy constructor
    aCA2.getdata()=10 // 经过情況 2 之后
    复制代码

      这里有几点需要注意的:

      1. 38行的将一个object指派(assign)给另一个object,这也是一种复制,但它唤起的所谓的copy assignment operator。本例并没有特别设计copy assignment operator;

      2.调用foo2()之前,_data为5,进入foo2()之后再输出,已变成10,可见copy constructor的确在foo2()的参数复制时发生;

      3.foo3()内有一个local object,其_data为3,把穹当做返回值输出,却变成了10,可见copy constructor的确在foo3()的返回值复制时发生;

     

  • 相关阅读:
    力扣刷题日志-Day2 (力扣151、43、14)
    hyper-v导入硬盘无法启动解决方法
    【c++】分蛋糕(数论)
    Django的初步学习及线上部署
    骨传导运动耳机排行榜,目前最好的几款骨传导耳机分享
    Linux ____02、Linux开关机、目录介绍、文件目录相关命令(常用命令)
    第六章《类的高级特性》第2节:包的创建和使用
    4、创建第一个鸿蒙应用
    adb操作及常用命令
    Bootstrap-- 媒体特性
  • 原文地址:https://www.cnblogs.com/ruanchunyi/p/18173186