精华内容
下载资源
问答
  • 在C++的多重继承中含有虚基类,成员对象时很容易把构造与析构的顺序弄不清楚 下面看一段代码 #include <iostream> using namespace std; class OBJ1 { public: OBJ1() { cout<<"OBJ1"<<endl; ...

    C++多重继承的构造与析构的次序

    在C++的多重继承中含有虚基类,成员对象时很容易把构造与析构的顺序弄不清楚

    下面看一段代码

    #include <iostream>
    using namespace std;
    class OBJ1
    {
    public:
        OBJ1()
        {
            cout<<"OBJ1"<<endl;
        }
        ~OBJ1()
        {
            cout<<"~OBJ1"<<endl;
        }
    };
    class OBJ2
    {
    public:
        OBJ2()
        {
            cout<<"OBJ2"<<endl;
        }
        ~OBJ2()
        {
            cout<<"~OBJ2"<<endl;
        }
    };
    class Base1
    {
    public:
        Base1()
        {
            cout<<"Base1"<<endl;
        }
        ~Base1()
        {
            cout<<"~Base1"<<endl;
        }
    };
    class Base2
    {
    public:
        Base2()
        {
            cout<<"Base2"<<endl;
        }
        ~Base2()
        {
            cout<<"~Base2"<<endl;
        }
    };
    class Base3
    {
    public:
        Base3()
        {
            cout<<"Base3"<<endl;
        }
        ~Base3()
        {
            cout<<"~Base3"<<endl;
        }
    };
    class Base4
    {
    public:
        Base4()
        {
            cout<<"Base4"<<endl;
        }
        ~Base4()
        {
            cout<<"~Base4"<<endl;
        }
    };
    class Derived: public Base1, virtual public Base2, public Base3, virtual public Base4
    {
    public:
        Derived():Base4(),Base3(),Base2(),Base1(),obj2(),obj1(){}
    protected:
        OBJ1 obj1;
        OBJ2 obj2;
    };
    int main()
    {
        Derived aa;
        return 0;
    }
    
    

    运行结果

    Base2
    Base4
    Base1
    Base3
    OBJ1
    OBJ2
    ~OBJ2
    ~OBJ1
    ~Base3
    ~Base1
    ~Base4
    ~Base2
    Program ended with exit code: 0
    

    由此可知C++中多重继承的构造顺序是
    1)虚函数优先构造,其顺序是按那个继承的顺序来的,而不是按初始化列表来的
    2)然后构造普通的基类,其顺序也是按那个继承的顺序来的,不是按初始化列表来的
    3)最后构造成员对象
    4)构造派生类自己的函数体

    由此可知C++中多重继承的析构顺序是
    1)构造派生类自己的函数体
    2)析造成员对象
    3)然后析构普通的基类,其顺序也是按那个继承的顺序来的,也不是按初始化列表来的
    4)最后是那个虚基类

    展开全文
  • [C++]类中构造与析构的顺序 转载自iyjhabc的博客,讲的十分通俗易懂,转载到自己的博客方便自己日后复习 首先我们结合一段示例代码来看 class A { public: A() { puts("In A"); } ~A() { puts("Out A"); } }...

    转载自iyjhabc的博客,讲的十分通俗易懂,转载到自己的博客方便自己日后复习

    首先我们结合一段示例代码来看

    class A
    {
    public:
        A()
        {
           puts("In A");
        }
        ~A()
        {
           puts("Out A");
        }
    };
    
    class B
    {
    public:
        B()
        {
           puts("In B");
        }
        ~B()
        {
           puts("Out B");
        }
    };
    
    class D
    {
    public:
        D()
        {
           puts("In D");
        }
        ~D()
        {
           puts("Out D");
        }
    };
    
    class X
    {
    public:
        X()
        {
           puts("In X");
        }
        virtual ~X()
        {
           puts("Out X");
        }
        virtual void test()
        {
           puts("test in X");
        }
        D d;
    };
    
    class C:public X
    {
    public:
        C()/*: b(), a()*/
        {
           puts("In C");
    
        }
        ~C()
        {
           puts("Out C");
        }
        virtual void test()
        {
           puts("test in C");
        }
    private:
        A a;
        B b;
    };
    
    int main()
    {
        X* p = new C;
        p->test();
        delete p;
    
        return 0;
    }
    

    在这里,类C公有继承类X,并且其中组合了类A和B的对象实例a和b。类X组合了类D的对象d,同时C和D在继承关系的同时,声明了虚函数test(涉及到多态的实现),X的析构函数声明为虚函数,后面会有讲解为何这么做。

    new C的时候,由于C由X继承而来,因此先构造X。首先,按照初始化列表初始化X的成员变量,这里没有初始化列表,系统也会默认地为d进行默认初始化,此时调用D的构造函数,打印In D。(先初始化成员,再运行构造函数)

    X的初始化阶段结束后,运行X的构造函数,打印In X。

    X部分构造结束后,开始构造C的部分。同样的先进行初始化工作,无论有无初始化列表,无论初始化列表的顺序如何,成员的初始化顺序都按声明顺序进行初始化。打印In A,In B。

    C的成员完成初始化后,调用C的构造函数,打印In C。

    /----------------------------------至此,new C的过程完毕-----------------------------------------/

    基类指针调用虚函数,进行动态绑定,输出test in C

    /----------------------------------开始对指针p析构-----------------------------------------/

    析构的顺序就是构造顺序的逆序。就是先析构子类,再析构父类(这里原博客说反了,感觉有失偏颇)。先析构本类,再析构本类的成员。

    于是打印的顺序为O C, O B, O A, O X, O D

    这个例子同时也解释了,为什么基类的析构函数要声明为虚函数。如果

    //不是虚函数 
     ~X()
     {
        puts("Out X");
     }
    

    那么delete p时候会直接析构p的静态类型X,所以C的成员以及C的析构函数将不会运行,这样就会造成内存泄露

    展开全文
  •  析构的顺序与构造的顺序严格相反。  这两种顺序决定了,在这两个函数中,子类中函数的多态性无法实现。构造的时候,基类子对象构造函数先执行,子类的特有部分尚未被构造,所以不能被调用。而在析构时,子类...

      子类对象构造的顺序:

      1. 按照继承表依次构造每个基类子对象;

      2. 按照声明的顺序依次构造每个成员对象;

      3. 执行函数体中的代码。

      析构的顺序与构造的顺序严格相反。

     

      这两种顺序决定了,在这两个函数中,子类中函数的多态性无法实现。构造的时候,基类子对象构造函数先执行,子类的特有部分尚未被构造,所以不能被调用。而在析构时,子类特有部分先被析构,在基类子对象中也无法调用。

      

      总算是忙完了,过两天正常的日子,接下来还要忙。

    转载于:https://www.cnblogs.com/itit/p/3422465.html

    展开全文
  • 关于C++继承体系中类的构造与析构的顺序1.构造函数先看下面的类定义class B1 class B2{ {public: public: 

                                              关于C++继承体系中类的构造与析构的顺序
    1.构造函数
    先看下面的类定义
    class B1                        class B2
    {                                {
    public:                                public:
            int i;                                int i;
            B1() { i = 0; }                 B2() { i = 0; }
            virtual void f() {}             virtual void f() {}
    } ;                               } ;

     

    class M1                        class M2
    {                                {
    public:                                public:
            int i;                                int i;
            M1() { i = 0; }                        M2() { i = 0; }
            virtual void mf() {}                virtual void mf() {}
    } ;                               };

    class C : public B1, public B2
    {
    public:
            virtual void f() {}
            M1 m1;
            M2 m2;
    };

    (1)编译器会不会为C生成默认构造(default ctor)
            编译器只在需要时才会为一个类生成def ctor。“需要时”指:
            a.一个类有虚函数
            b.一个类有虚基类
            c.一个类的基类有def ctor
            d.一个类的成员类有def ctor
            在这里,class C符合c,d两个条件,编译器会为它生成def ctor.

    (2)默认构造的内容
            编译器生成的def ctor是这样的
            C::C()
            {
                    B1::B1()
                    B2::B2()
                    设定虚表指针指向C的虚表
                    m1::M1()
                    m2::M2()
            }
            a.按声明顺序构造基类
            b.设定虚表指针
            c.按声明顺序构造成员类

    (3)对自定义构造函数的改造
            对于B1,B2,M1,M2,已有构造函数,但编译器会对其改造,加入一些代码,完成必要的初始化工作。改造后的ctor如下:(以B1为例)
            B1::B1()
            {
                    设定虚表指针指向B1的虚表
                    i = 0;
            }
            a.设定虚表指针
            b.如果用户写有代码(如i=0),则执行这些代码

    (4)综合(2),(3),构造函数完整代码如下
            T::T()
            {
                    按声明顺序构造基类
                    设定虚表指针
                    按声明顺序构造成员类
                    如果用户写有代码,则执行这些代码
            }
            在用户代码执行前,基类与成员类已构造完毕,虚指针已设定。

            所以C的构造顺序是:B1-〉B2-〉虚指针设定-〉m1-〉m2->C自己的代码(如果有的话)
            由(3)可知,每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表,然后在C::C中,虚指针被重新设定为C的虚表。
            在继承体系中,构造是由上而下的,B1或B2构造时,C还未构造。C构造时,B1和B2已构造完成。

            所以在继承体系中,一个类的构造函数执行时,该类的所有基类已构造完毕,虚指针已设定,所有成员类也已构造完毕。但该类的所有子类均未构造完成。

     

     

    2.析构函数
    (1)编译器会不会为C生成析构函数(dtor)
            编译器只在需要时才会为一个类生成dtor。“需要时”指:
            a.类的基类有析构
            b.类的成员类有析构
            与构造不同,少了两点,因为在一个类析构时,这是一个破坏操作(destory),既然类以已经没用了,何必再把虚指针设一下呢。

     

    (2)析构的内容
            编译器生成的析构是这样的
            C::~C()
            {
                    m2::M2()
                    m1::M1()
                    B2::B2()
                    B1::B1()
            }
            a.按声明顺序相反的顺序析构成员类
            b.按声明顺序相反的顺序析构基类
            这里的相反顺序是C++标准规定的要求,不同编译器都应做到,否则不符合标准(实际上编译器或多或少都有不符标准的地方,当然不是指这里所说的顺序问题)

    (3)完整的析构的调用顺序
            对于已有析构函数的类,编译器会对其改造,加入一些代码,完成必要的工作。改造后的dtor如下:
            T::~T()
            {
                    设定虚表指针指向T的虚表
                    执行用户代码
                    按声明顺序相反的顺序析构成员类
                    按声明顺序相反的顺序析构基类
            }
            这是完整的析构的调用顺序
            在用户代码执行前,虚指针已重新设定,以便用户代码能正确执行。然后再析构成员类和基类。

            所以,如果上面例子中的五个类都有析构函数的话,调用顺序是:
            虚指针设定-〉C自己的代码->m2-〉m1-〉B2-〉B1
            每个类的dtor中,都会暂时将虚指针指向自己的虚表。调用C::~C时,虚指针指向C的虚表,然后在B1::~B1中,虚指针被重新设定为B1的虚表。

            可见,在继承体系中,析构是由下而上的,B1或B2析构时,C已被析构。C析构时,B1和B2还未被析构。
            所以在继承体系中,一个类的析构函数执行时,该类的所有基类还未被析构,所有成员类也未被析构。但该类的所有子类均已析构完成。

     

    3.虚函数
            在构造或析构中调用虚函数,会怎样.如:
            B1::B1()
            {
                   f();
            }
            调用的是B1::f()还是C::f()
            答案是B1::f()
            每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表
            所以是B1::f()
            如果在C的构造函数中调用f(),那就调用C::f()

     
    展开全文
  • 假设A是基类, B是派生类, ...构造B对像,从产生到消失流程 A() -> B() -> ~B() -> ~A()..即符合Last-In-First-Out (LIFO) #include <map>#include <string>#include <iostream>cla...
  • 定义一个类对象时,首先根据初始化列表初始化类成员(就算没有显式定义初始化列表,编译器也会默认地初始化一次),然后运行构造函数。因此,类成员的构造函数必定先于类的构造函数运行。 class A { public: ...
  • 构造与析构顺序

    2019-10-24 13:55:05
    构造与析构在C++中,有一种特殊成员函数,它名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象时自动执行。这种特殊成员函数就是构造函数(Constructor)。 初始化对象...
  • #include //多个对象构造析构//1)当类中有成员变量是其它类对象时,首先调用成员变量的构造函数,调用顺序与声明顺序相同;//之后调用自身类的构造函数//2)析构函数调用顺序与对应的构造函数调用顺序相反///...
  • 1.示例说明 以下面程序说明函数执行顺序: 1)定义String类,构造析构会打印其中字符串 2)定义一个基类Base,类中含有两个String 3)从基类Base派生出Derive类,类中含有一个String 每个类函数成员...
  • C++全局变量与静态变量的构造与析构顺序 本人是C++初学者,将自己学习过程中一些收获记录在CSDN上,一方面能为大家提供帮助,另一方面能够方便自己日后查看。 关于在C++全局变量与静态变量的构造与析构顺序,...
  • 本文主要通过实例总结各种情况下的构造与析构顺序。 继承 场景:B类继承两个父类A和C,每个类构造函数和析构函数很简单,就是打印对应函数名,以便观察构造及析构函数执行顺序。 #include using namespace std;...
  • 那么当创建B对象时,AB的构造析构的顺序是谁先谁后? 示例: class Phone { public: Phone(string name) { m_PhoneName = name; cout << "Phone构造" << endl; } ~Phone() {
  • 的构造与析构顺序

    2020-11-17 21:11:45
    首先,我们来看一段代码: #include using namespace std; class A { public: A() { cout ; } ~A() { cout ; } };... (4)以上所有对象的析构函数以与构造函数的执行顺序相反的顺序执行。
  • 首先要明确一条原则:构造与析构的顺序是相反的。 个人总结,在这个知识点的理解和掌握上,有四层“境界”: 第一层:理解一个类内部各个数据成员之间的构造/析构顺序。这里需要注意的是,数据成员的构造顺序与其在...
  • //继承时,构造析构的顺序:先父母,再客人,后自己#include <iostream>using namespace std;class Object { public: Object(const char* s) { cout (): " ; } ~Object() { cout
  • 单继承派生类只有一个...然后按照派生类数据成员声明的顺序,依次初始化数据成员或调用相应构造函数,对象成员无论列出与否总会被构造;在然后最后执行派生类的构造函数体。 析构构造的逆序,所以反过来就行。 ...
  • 1、代码如下: class A { public: ...5、碰到过这样情况,vs自动生成的析构方法有问题,导致崩溃。手动添加一个析构方法,就可以了。 转载于:https://www.cnblogs.com/nzbbody/p/4540717.html
  • C++ 构造析构的顺序

    2009-03-23 09:50:00
    析构与构造时完全相反顺序。 baseC() base() derivedC() derived() ~derived() ~derivedC() ~base() ~baseC() */ #include<iostream> usingnamespacestd; classbase...
  • 我们可以通过代码1所示示例程序观察到C++中一个关于全局类变量初始化顺序的有趣现象。  class1.cpp   #include      cla
  • C++类构造与析构顺序

    2020-09-11 18:16:35
    1.所有基类的构造函数必须被调用 1)如果基类在构造函数初值列中,必须提供基类构造函数所需参数 2)如果基类不在初值列中,将调用基类默认构造函数
  • 建立一个类继承类,然后实例化之后来观察该对象的构造析构函数调用顺序。 首先我们建造一个People.h头文件: #ifndef INHERIT_PEOPLE_H #define INHERIT_PEOPLE_H #include &lt;string&gt; using...
  • 构造函数执行顺序: (1)存在继承关系时,先执行父类的构造函数,再执行子类的构造函数; (2)当一个类中含有对象成员时,在启动本类...(3)对于非静态局部对象,他们析构函数执行顺序与构造函数相反。
  • 父类与子类的构造与析构顺序 继承中的顺序如下: 先构造父类,在构造子类 析构顺序与构造顺序相反 #include using namespace std; class Base1 { public: Base1() { cout构造函数"; } ~Base1() { cout析构函数"; } }...
  • 构造顺序与析构顺序

    千次阅读 2019-03-19 10:33:25
    1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右); 2. 调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序; 3. 派生类自己构造函数体中的内容。 派生类析构函数执行的次序: ...
  • /*构造时:先成员,后构造函数,然后处理父类析构与构造时完全相反顺序。baseC()base()derivedC()derived()~derived()~derivedC()~base()~baseC()*/#include using namespace std;class baseC{public:

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 742
精华内容 296
关键字:

构造与析构的顺序