精华内容
下载资源
问答
  •  在说构造函数之前我们得先弄明白几个问题,首先是什么是类的构造函数,什么是类的成员对象,什么是基类,然后我们再来说构造函数的调用顺序。  1、 类的构造函数  构造函数的功能主要用于在类的对象创建时定义...
  •  在说构造函数之前我们得先弄明白几个问题,首先是什么是类的构造函数,什么是类的成员对象,什么是基类,然后我们再来说构造函数的调用顺序。  1、 类的构造函数  构造函数的功能主要用于在类的对象创建时定义...
  • 《面向对象程序设计基础(第二版》李师贤等,第254页:C++语言的基本规则是:创建一个派生类的对象时,如果基类带有构造函数,则先调用基类的构造函数,然后才调用派生类的构造函数。  《Thinking in C++》,刘...

    《面向对象程序设计基础(第二版》李师贤等,第254页:C++语言的基本规则是:创建一个派生类的对象时,如果基类带有构造函数,则先调用基类的构造函数,然后才调用派生类的构造函数。
      《Thinking in C++》,刘宗田等译,第261页:可以看出,构造在类层次的最根处开始,而在每一层,首先调用基类构造函数,然后调用成员对象构造函数。
      《C++ Primer Plus(第四版)中文版》,孙建春等译,第399页:记住:创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。
      真的是这样吗?
      一个类的对象在实例化时,这个类的构造函数会被调用。如果承认这一点,就会发现上述论断的矛盾之处。一个派生类的对象,在实例化时,不调用作为产生它的类 的构造函数,而先去调用别的类的构造函数,这符合逻辑吗?再考虑一下基数构造函数有参数的的时候,派生类构造函数的定义形式,“派生类构造函数可以使用初 始化列表机制将值传递给基类构造函数”(《C++ Primer Plus(第四版)中文版》第399页)。如果是基类的构造函数先被调用,那么它所使用的参数从何而来?
      前两本书在说明这一规则时,毫无例外地在派生类构造函数和基类构造函数中使用cout输出一些信息来表明相应的构造函数被调用了,并以此说明构造函数的调 用顺序。在这里,我要指出的是:这一顺序,仅仅是这些cout输出的顺序,并不能说明是函数调用的顺序。真正调用的过程,单纯依赖于C++是看不到的。
      我们可以用这样的实验来证明这一点。选择前两本书关于这一规则的任何一个实例,在Visual Studio中,分别对派生类和基类的构造函数下断点,注意:断点要下在函数定义函数名处,这样才是真正函数执行的起点,而不能下在cout语句上,那是 函数体,不能说明问题。然后调试这个程序,你会发现派生类构造函数的断点先中断,基类的构造函数断点后中断。如果你有汇编的知识,那么请打开汇编语言的开 关,这之间的关系就更明显了。
      现在可以更确切地说明这个规则了:派生类对象在实例化时,派生类构造函数先被执行,在执行过程中(在实例化派生类成员前),调用基类构造函数,然后(在基类成员实例化后)返回派生类构造函数实例化派生类成员。
      析构函数的顺序问题依此类推。

     

    // 程序7.3.1
    // 程序:SEQUENCE.CPP
    // 功能:演示继承关系中基类与派生类的构造函数与析构函数的调用次序。

    #include <iostream.h>

    class BASE {
    public:
    // 构造函数
    BASE()
    {
    cout << "Constructing base object./n";
    }
    // 析构函数
    ~BASE()
    {
    cout << "Destructing base object./n";
    }
    };

    class DERIVED: public BASE {
    public:
    // 构造函数
    DERIVED()
    {
    cout << "Constructing derived object./n";
    }
    // 析构函数
    ~DERIVED()
    {
    cout << "Destructing derived object./n";
    }
    };

    DERIVED obj; // 声明一个派生类的对象

    int main()
    {

    // 什么也不做,仅完成对象obj的构造与析构
    return 0;
    }

    上面DERIVED obj;这一语句对应的汇编代码是:
    35: DERIVED obj; // 声明一个派生类的对象
    00401080 push ebp
    00401081 mov ebp,esp
    00401083 sub esp,40h
    00401086 push ebx
    00401087 push esi
    00401088 push edi
    00401089 lea edi,[ebp-40h]
    0040108C mov ecx,10h
    00401091 mov eax,0CCCCCCCCh
    00401096 rep stos dword ptr [edi]
    00401098 mov ecx,offset obj (00428ab8)
    0040109D call @ILT+15(DERIVED::DERIVED) (00401014) 
    004010A2 pop edi
    004010A3 pop esi
    004010A4 pop ebx
    004010A5 add esp,40h
    004010A8 cmp ebp,esp
    004010AA call __chkesp (00403570)
    004010AF mov esp,ebp
    004010B1 pop ebp
    004010B2 ret
    请注意加粗的那一行,一切就清楚了。

    展开全文
  • 关于C++中构造函数的调用顺序

    千次阅读 2013-04-05 15:50:46
    虚基类构造函数如果有多个,虚基类则构造函数的调用顺序是某类在类派生表中出现的顺序而不是它们在成员初始化表中的顺序;  2、如果派生类中的基类有虚基类的话,而且该派生类不止有一个基类,那么优先调用虚...

    1、虚基类的构造函数;虚基类构造函数如果有多个,虚基类则构造函数的调用顺序是某类在类派生表中出现的顺序而不是它们在成员初始化表中的顺序; 


    2、如果派生类中的基类有虚基类的话,而且该派生类不止有一个基类,那么优先调用虚基类的构造函数,先按照派生列表的顺序调用虚基类的构造函数,等虚基类的构造函数全部调用完,再按照派生列表的顺序调用非虚基类的构造函数


    3、创建派生类的对象,基类的构造函数函数优先被调用(也优先于派生类里的成员类);基类如果有多个,则构造函数的调用顺序是某类在类派生表中出现的顺序而不是它们在成员初始化表中的顺序; 

    4、如果类里面有成员类,成员类的构造函数优先被调用;成员类对象构造函数如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序而不是它们出现在成员初始化表中的顺序; 

    5、从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但仅仅用建立对象的最远派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。


    6、派生类构造函数 

    作为一般规则派生类构造函数应该不能直接向一个基类数据成员赋值而是把值传递 给适当的基类构造函数否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。(基类设计者的责任是提供一组适当的基类构造函数)



    实例1:

    class F : virtual public A, public D ,virtual public C,public B
    {
    public:     
    F():C(),A(),B(),D() //其中,虚基类 A,C必须在初始化列表中(第5条)
        {
         ....
        }
         
    };



    当产生F类对象,构造函数调用顺序:
    1)调用 A类构造函数,在调用C类构造函数 (第2条,先调虚基类,第1条,顺序为派生表中顺序,而非初始化列表中的顺序)
    2)调用 D类构造函数,在调用B类构造函数(第2条,再调虚基类,第1条,顺序为派生表中顺序,而非初始化列表中的顺序)

    实例2:

    class A 
    {
    public: 
       A();
    }
    class B 
    {
    public: 
       B();
    }
    
    class F : public D ,virtual public C
    {
    public:     
        F():C(),D()
       {
            ....
       }
    public:
     A a;
     B b;  
    };
    


    当产生F类对象,构造函数调用顺序:
    1)先调用C类构造函数,再调用D类构造函数 (第3条,基类构造函数 先于 成员变量构造函数)
    2)然后调用A类构造函数、B类构造函数(变量a 先于 b申明)
    3)最后调用F类的构造函数。(成员类构造函数先于本类,所以F类最后调用)





    展开全文
  • 关于构造函数的调用顺序

    千次阅读 2009-08-27 10:48:00
    在说构造函数之前我们得先弄明白几个问题,首先是什么是类的构造函数,什么是类的成员对象,什么是基类,然后我们再来说构造函数的调用顺序。 1、 类的构造函数 构造函数的功能主要用于在类的对象创建时定义初始化...

     

    作者:王姗姗, 华清远见嵌入式学院讲师。

    在说构造函数之前我们得先弄明白几个问题,首先是什么是类的构造函数,什么是类的成员对象,什么是基类,然后我们再来说构造函数的调用顺序。

        1、 类的构造函数
        构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰,这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择。构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法在程序执行到它的时候被调用。一个类可以有多个构造函数,根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载。

        2、 类的成员对象

        类包含数据成员和成员函数。当类中的成员数据是另一个类的对象时,我们就说这个类是该类的成员对象。

        3、 基类

        通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。

        4、 构造函数的调用顺序

        我们来看下面一段代码:
        class B1
        {
        public:
             B1(int i) {cout<<"constructing B1 "<<i<<endl;}
        };
        class B2
        {
         public:
              B2(int j) {cout<<"constructing B2 "<<j<<endl;}
         };
         class B3
         {
         public:
             B3( ){cout<<"constructing B3 *"<<endl;}

        };
        class C: public B2, public B1, public B3
        {
        public:
             C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
        private:
             B1 memberB1;
             B2 memberB2;
             B3 memberB3;
        };
        void main( )
         { C obj(1,2,3,4); }
        运行后的结果如下:
        constructing B2 2
        constructing B1 1
        constructing B3 *
        constructing B1 3
        constructing B2 4
        constructing B3 *
        为什么会有以上的结果?

    众所周知构造函数的执行次序如下:

    调用基类构造函数,调用顺序按照他们的继承时声明的顺序。

    调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。

    派生类的构造函数体中的内容。

    析构函数的调用顺序相反。
        那么再来看以上的例子就很容易理解了。B2、B1、B3是C的基类,按照上述的顺序,我们先要构造基类,然后才是子对象,最后是其本身的构造函数所以先要执行这三个类的构造函数。在构造时按照他们在类中的顺序,首先调用B2的构造函数
        B2(int j) {cout<<"constructing B2 "<<j<<endl;}
        由于在默认参数列表
        C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
        中,将b的值传给了B2的构造函数,b为2,故打印出:
        constructing B2 2
        接下来要构造的是B1了。显然在C的默认参数构造列表中将a的值传给了B1,
        所以打印出:
        constructing B1 1
        B3在构造时没有传递参数,调用B3( ){cout<<"constructing B3 *"<<endl;}
        打印出:
        cout<<"constructing B3 *
        这时基类的构造函数已经执行完毕,接着该处理内嵌成员对象的构造函数了。
        我们看到C类有三个对象:B1 memberB1;B2 memberB2;B3 memberB3;,按照构造函数的调用顺序,我们需要按照他们在类中声明的顺序来分别构造memberB1、memberB2、 memberB3。在默认的参数列表中,用c来构造了memberB1,用d来构造memberB2,
        故打印出:
        constructing B1 3
        constructing B2 4
        constructing B3 *
        最后调用本身的构造函数,由于函数体为空,故什么也没有打印出来。
        总结下来,我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数的调用顺序为:调用基类的构造函数->调用成员对象的构造函数->调用自身的构造函数。构造函数的调用次序完全不受构造函数初始化列表的表达式中的次序影响,与基类的声明次数和成员对象在函数中的声明次序有关。

    展开全文
  • 先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决;构造函数的调用顺序总是如下: 1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生...
      先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决;构造函数的调用顺序总是如下:
    1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。
    2.成员类对象构造函数。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。
    3.派生类构造函数。

    析构函数
        析构函数的调用顺序与构造函数的调用顺序正好相反,将上面3个点反过来用就可以了,首先调用派生类的析构函数;其次再调用成员类对象的析构函数;最后调用基类的析构函数。
        析构函数在下边3种情况时被调用:
        1.对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数);
        2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
        3.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

    下面用例子来说说构造函数的的调用顺序:
    #include "stdafx.h"
    #include "iostream"
    using namespace std;
    class Base
    {
    public:
        Base(){ std::cout<<"Base::Base()"<<std::endl; }
        ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
    };

    class Base1:public Base
    {
    public:
        Base1(){ std::cout<<"Base1::Base1()"<<std::endl; }
        ~Base1(){ std::cout<<"Base1::~Base1()"<<std::endl; }
    };

    class Derive
    {
    public:
        Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
        ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
    };

    class Derive1:public Base1
    {
    private:
        Derive m_derive;
    public:
        Derive1(){ std::cout<<"Derive1::Derive1()"<<std::endl; }
        ~Derive1(){ std::cout<<"Derive1::~Derive1()"<<std::endl; }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        Derive1 derive;
        return 0;
    }

    运行结果是:
    Base::Base()
    Base1::Base1()
    Derive::Derive()
    Derive1::Derive1()
    Derive1::~Derive1()
    Derive::~Derive()
    Base1::~Base1()
    Base::~Base()

    那么根据上面的输出结果,笔者稍微进行一下讲解,构造函数的调用顺序是;首先,如果存在基类,那么先调用基类的构造函数,如果基类的构造函数中仍然存在基类,那么程序会继续进行向上查找,直到找到它最早的基类进行初始化;如上例中类Derive1,继承于类Base与Base1;其次,如果所调用的类中定义的时候存在着对象被声明,那么在基类的构造函数调用完成以后,再调用对象的构造函数,如上例中在类Derive1中声明的对象Derive m_derive;最后,将调用派生类的构造函数,如上例最后调用的是Derive1类的构造函数。

    virtual析构函数
    下面来说一说为多态基类声明virtual析构函数:
    在C++中,构造函数不能声时为虚函数,这是因为编译器在构造对象时,必须知道确切类型,才能正确的生成对象,因此,不允许使用动态束定;其次,在构造函数执行之前,对象并不存在,无法使用指向此此对象的指针来调用构造函数,然而,析构函数是可以声明为虚函数;C++明白指出,当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义---实际执行时通常发生的是对象的derived成分没被销毁掉。

    看下面的例子:
    class Base
    {
    public:
        Base(){ std::cout<<"Base::Base()"<<std::endl; }
        ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
    };

    class Derive:public Base
    {
    public:
        Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
        ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        Base* pBase = new Derive(); 
        //这种base classed的设计目的是为了用来"通过base class接口处理derived class对象"
        delete pBase;

        return 0;
    }

    输出的结果是:
    Base::Base()
    Derive::Derive()
    Base::~Base()
    从上面的输出结果可以看出,析构函数的调用结果是存在问题的,也就是说析构函数只作了局部销毁工作,这可能形成资源泄漏败坏数据结构等问题;那么解决此问题的方法很简单,给base class一个virtual析构函数

    class Base
    {
    public:
        Base(){ std::cout<<"Base::Base()"<<std::endl; }
        virtual ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
    };

    class Derive:public Base
    {
    public:
        Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
        ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        Base* pBase = new Derive();
        delete pBase;

        return 0;
    }

    输出结果是:
    Base::Base()
    Derive::Derive()
    Derive::~Derive()
    Base::~Base()
    可能上面的输出结果正是我们所希望的吧,呵呵!由此还可以看出虚函数还是多态的基础,在C++中没有虚函数就无法实现多态特性;因为不声明为虚函数就不能实现“动态联编”,所以也就不能实现多态啦!
    展开全文
  • 构造函数 先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何...如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。3.派生类...
  • 近期在网上搜寻了一下关于C++派生类构造函数的调用问题,大部分博文给出的顺序是: 1、基类构造 2、成员构造 3、派生构造。 这个顺序严格来讲是错误的,对新手极具误导性!  依据侯捷翻译的Stanley B. Lippman...
  • 构造函数    先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用...如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始
  • 构造函数先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决;构造函数的调用顺序总是如下: 1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类...
  • 在这里我会介绍一些关于C++类的一些特性,其中包括类的四大默认函数,构造函数、拷贝构造函数、赋值构造函数和析构函数,以及...4、构造函数的功能是由用户定义的,用户根据对象初始化的要求设计函数体和函数参数...
  • 构造函数 先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造...如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序...
  • 如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。  3.派生类构造函数。  析构函数  析构函数的调用顺序与构造函数的调用顺序正好相反,将上面3个...
  • C++ 派生类的析构函数的调用顺序为:A) 基类、派生类和对象成员类的析构函数  B) 派生类、对象成员类和基类的析构函数  C) 对象成员类、派生类和基类的析构函数  D) 派生类、基类和对象成员类的析构函数 答案是...
  • 构造函数    先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用...如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始
  • 在学习C++的类时,碰到了构造函数,而关于构造函数又引申出了复杂情况下构造函数的调用顺序。看视频讲解、敲示例代码,算是大概清楚了调用次序,为了今后查询,特意在此记录一下。 构造函数:创建类对象时,初始...
  • 存在类内方法外,作用是对象的预加载,每当new调用构造方法时候 会优先执行构造代码块,每new一次就优先执行一次构造代码块 普通代码块: 存在成员方法中,作用是控制变量生命周期,执行完后立即消失.构...
  • 创建子类对象时,将调用子类的构造函数,而不是父类的构造函数。子类的构造函数将使用父类的一个构造函数。这种顺序不同于继承机制。因此,子类不继承父类的构造函数,所以将类的构造函数声明为虚函数没有意义。这个...
  • 2)在创建一个对象时,构造函数就自动执行,但是在声明一个类的指针对象时,构造函数不会被调用,当new一个空间的时候,构造函数才会被调用 3)构造函数一般用来对数据成员的赋值,这也是它的一般性作用 4)构造...
  • 构造块:即成员代码块(写在类体中代码块),优先级次于静态块,高于构造函数,且每次创建新对象都会执行 构造函数:优先级次于静态块、构造块,每次创建新对象都会调用 普通快:最没地位,优先级最低 总结: ...
  • 构造函数与析构函数

    2017-02-03 17:01:45
    构造函数    先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用...如果有多个成员对象构造函数的调用顺序对象在类中被声明的顺序,而不是它们出现在成员初始

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 169
精华内容 67
关键字:

关于对象成员的构造函数的调用顺序