构造函数和析构函数的调用过程

来源:互联网 发布:c语言开平方怎么写 编辑:程序博客网 时间:2024/04/29 05:39
下面代码的输出是什么?(D)
class A  {  public:      A()  {     }      ~A() {    cout<<"~A"<<endl;   }  };     class B:public A  {      public:          B(A &a):_a(a)          {                       }          ~B()          {              cout<<"~B"<<endl;          }      private:          A _a;      };         int main(void)   {          A a;       //很简单,定义a的时候调用了一次构造函数          B b(a); }
A、~B
B、~B ~A
C、~B ~A ~A

D、~B ~A ~A ~A



been:
B(A &a):_a(a)
这个引用也是值得注意的,这里提醒大家一下,去掉引用,第一个析构是是~A


灵灵:
下面以例子说明参数为类对象,是否有初始化列表时构造与析构函数的执行顺序:

#include <iostream>using namespace std;   class A{public: A(){ cout<<"A"<<endl; }        A(const A& other){ cout<<"copy A"<<endl;}        ~A() { cout<<"~A"<<endl; } };class B:public A{ public:     B(A a):_a(a) //  B(A &a):_a(a)  会有完全不同的结果    {     cout<<"B"<<endl;    }     ~B() { cout<<"~B"<<endl; }private: A _a;}; void main(void){     A a;     B b(a);     return; }  运行结果:A            //A a  创建对象a执行A类的构造           copy A       //B b(a)  传参的时候利用A类的复制构造             A          //创建对象B调用基类A的构造             copy A     //使用初始化列表初始化成员_a利用A类的复制构造             B          //调用自己的构造             ~A         //析构参数             ~B         //析构自身             ~A         //析构B类成员             ~A         //再析构基类的             ~A         //析构 对象a class A{public: A(){ cout<<"A"<<endl; }        A(const A& other){ cout<<"copy A"<<endl;}        ~A() { cout<<"~A"<<endl; } };class B:public A{ public:     B(A a)  //若此处换为:B(A &a),则不调用构造函数    {     cout<<"B"<<endl;    }     ~B() { cout<<"~B"<<endl; }private: A _a;}; void main(void){     A a;          B b(a);     return; }   运行结果:A            <span style="font-family: Arial, Helvetica, sans-serif;">//A a  创建对象a执行A类的构造</span>           copy A       //B b(a)  传参的时候利用A类的复制构造             A          //创建对象B调用基类A的构造             A          //初始化B类的成员执行A类的构造             B          //调用自己的构造             ~A         //析构参数             ~B         //析构自身             ~A         //析构B类成员             ~A         //再析构基类的             ~A         //析构 对象a



vzhuzhu:
对于构造函数:基类构造函数 > 子类成员变量构造函数 > 子类构造函数
对于析构函数:子类析构函数 > 子类成员变量析构函数 > 基类析构函数
可以看出构造函数的调用过程和析构函数的调用过程正好相反。



水泽渊:
在vs上运行之后发现,输出顺序确实如@Aesthetic92所说,然而@赖聪林说的也没有错,可是@赖聪林举的例子并没有用到拷贝构造函数。
原题-----------------------------------------------------------------------------

class A{     public:     A() { cout<<"A"<<endl; }     ~A() { cout<<"~A"<<endl; } }; class B:public A {      public: B(A &a):_a(a)     {         cout<<"B"<<endl;     }      ~B() { cout<<"~B"<<endl; } private: A _a; };  void main(void) {     A a;     B b(a); }// 结果为AAB~B~A~A~A 

加上拷贝构造函数----------------------------------------------------------------------
<pre name="code" class="cpp"> class A {public: A(){ cout<<"A"<<endl; }         A(const A& other){ cout<<"copy A"<<endl;}         ~A() { cout<<"~A"<<endl; } }; class B:public A { public:     B(A &a):_a(a)     {     cout<<"B"<<endl;     }      ~B() { cout<<"~B"<<endl; } private: A _a; };  void main(void) {     A a;          B b(a); } //结果显示AAcopy AB~B~A~A~A 

修改后----------------------------------------------------------------------------------
class A { public:      A() { cout<<"A"<<endl; }      A(const A& other){ cout<<"copy A"<<endl;}      ~A() { cout<<"~A"<<endl; } }; class B:public A { public:     B(A &a) { cout<<"B"<<endl; }     ~B() { cout<<"~B"<<endl; } private: A _a; }; void  main(void) {    A a;    B b(a); } //结果为AAAB~B~A~A~A



Aesthetic92:
答案:选D
答案解析:答案看起来可能比较怪,其实给默认构造函数补上输出,然后再在基类里写个复制构造函数,这样结果就很明朗了;
首先 A a;这个调用A的默认构造函数,
B b(a); 因为A &a,形参为引用,不需要调用基类复制构造函数(其实基类也没写);_a(a)首先创建对象_a,调用基类A的默认构造函数,然后调用基类复制构造函数(测试的时候可以写出来),把a对象赋给_a,结束之后调用B的析构函数,输出~B;然后调用基类A的析构函数撤销复制构造函数,输出~A;调用基类A的析构函数撤销_a,输出~A;最后调用基类A的析构函数撤销一开始创建的A a,输出~A



kuring:
要想搞明白该问题,需要理解基类构造析构函数、子类构造析构函数和子类成员变量构造析构函数的调用顺序。
对于构造函数:基类构造函数 > 子类成员变量构造函数 > 子类构造函数
对于析构函数:子类析构函数 > 子类成员变量析构函数 > 基类析构函数
可以看出构造函数的调用过程和析构函数的调用过程正好相反。

main函数中首先构造变量a,然后是b。在构造b时首先调用b的基类A的构造函数,然后调用b中成员变量_a的构造函数,最后调用b的构造函数。
main函数调用结束返回时,变量的释放顺序跟变量的构造顺序正好相反。首先释放变量b,然后是变量a。
在释放变量b时,首先调用b的析构函数,然后析构变量b的成员_a,析构_a时调用_a的析构函数。再调用b的基类的析构函数。
然后是释放变量a,调用a的析构函数。

本例子中应该将A的析构函数更改为virtual的,防止使用多态机制时出现子类对象无法释放的情况,本例子中没有用到多态机制,不存在该问题。



赖聪林:

# include <iostream>using namespace std;class A  {  public:      A()  {  cout<<"create A"<<endl;   }      A(const A& other){ cout<<"copy A"<<endl;} //复制构造函数    ~A() {    cout<<"~A"<<endl;   }  }; class C{public:    C()  {  cout<<"create C"<<endl;   }     C(const A& other){ cout<<"copy C"<<endl;} //复制构造函数    ~C() {    cout<<"~C"<<endl;   }  };class B:public A  {  public:      B()    {          cout<<"create B"<<endl;    }      ~B()      {          cout<<"~B"<<endl;      }  private:      C _a; };         int main(void)  {        B b;         cout<<"------------------------"<<endl;} //上面的输出结果为create Acreate Ccreate B------------------------~B~C~A

我们可以看到,这个地方先是调用parent class的构造函数,然后对成员变量C类型的构造函数,然后再最后执行B类型的构造函数。
析构的过程就是上面的过程反过来。

所以Aesthetic92的解释有一部分不是很准确。我认为。

更加准确的说明应该是,
最开始析构b,~B,这个是没有争议的。
接着是析构b的成员变量_a,所以是~A
接着是b的parent class(基类)的~A
最后才是a的析构~A
不过为了理解这道题,我感觉配上我的例子更好理解一点,原题的成员变量和基类都是相同的类型,比较难以辨认。

0 0