C ++隐式和显式继承构造函数调用

时间:2015-05-19 18:33:08

标签: c++ inheritance implicit explicit

我对基础构造函数的隐式和显式调用有疑问。如果我们有这样的类层次结构:

class Person{
    protected:
        std::string m_name;
    public:
        Person(std::string& _name) : m_name(_name){std::cout << "A person is being constructed." << std::endl;}
};

class Baby : public Person{
    private:
        int m_no_of_nappies;
    public:
        Baby(std::string& _name, int& _no_of_nappies) : m_no_of_nappies(_no_of_nappies), Person(_name) {std::cout << "A baby is being constructed." << std::endl ;}

};

根据我的讲义,主要打电话给'Baby',如下:

std::string babyname = "Robert";
int nappies = 5;

Baby baby(babyname, nappies);

导致以下情况发生:

  1. 在Baby的初始化列表中明确调用Person: Baby的初始化列表被称为no_of_nappies被初始化。
  2. 接下来,调用Person的构造函数并调用 Person的初始化列表。 m_name已初始化。
  3. 然后调用Person的构造函数体。
  4. 最后调用Baby的构造函数体。
  5. 然而,这是否有意义,如果对父类的默认构造函数进行了隐式调用,如下所示:

    class Vehicle{
        protected:
            int m_no_wheels;
        public:
            Vehicle() : m_no_wheels(0) { std::cout << "A vehicle is being constructed." << std::endl; }
    };
    
    class Bicycle : public Vehicle{
        protected:
            bool m_is_locked;
        public:
            Bicycle() : m_is_locked(false) { std::cout << "A bicycle is being constructed." << std::endl; }
    };
    

    这是我不太确定的部分。我最好的猜测是,对主要版本中Bicycle bike;的调用会产生以下影响:

    1. 从Bike隐式调用Vehicle的默认构造函数。 在调用Bike的初始化列表之前。
    2. 由于车辆不从任何东西继承,称为车辆的初始化列表,其中m_no_wheels初始化为0
    3. 调用Vehicle的构造函数体。
    4. 我们返回自行车,现在将其初始化列表称为,将m_is_locked初始化为false
    5. 调用Bike的构造函数体。
    6. 有人可以解释一下隐含电话背后的推理是否正确吗?

      在我看来,主要区别在于,通过明确引用基本构造函数,始终首先命中子类的初始化列表,以便调用该基本构造函数 - 但是,通过隐式调用,最顶级的父母的初始化列表总是先命中。

      谢谢,非常感谢!

      编辑:我具体询问订单是否发生变化,具体取决于对父类的隐式或显式调用。

2 个答案:

答案 0 :(得分:3)

基础和成员的初始化顺序在[class.base.init] / 11中指定,您可以在此处找到摘要:http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order

  

列表中成员初始值设定项的顺序无关紧要:初始化的实际顺序如下:

     
      
  1. 如果构造函数是用于派生程度最高的类,则虚拟基类按它们出现在基类声明的深度优先从左到右遍历的顺序进行初始化(从左到右指的是出现在基本说明符列表中)
  2.   
  3. 然后,直接基类按从左到右的顺序初始化,因为它们出现在此类的基本说明符列表中
  4.   
  5. 然后,按照类定义中的声明顺序初始化非静态数据成员。
  6.   
  7. 最后,执行构造函数的主体
  8.         

    (注意:如果初始化顺序由不同构造函数的成员初始化程序列表中的外观控制,那么析构函数将无法确保销毁顺序与构造顺序相反)

在定义任何构造函数之前,初始化顺序是固定的;构造函数初始化列表仅影响如何基础和成员的初始化,而不是它们的初始化顺序。

由于PersonBaby的基础,因此它始终会在Baby的成员m_no_of_nappies之前初始化。作为Person初始化的一部分,初始化其自己的成员,然后执行其构造函数体。在Person的构造函数体返回后,m_no_of_nappies被初始化。 (销毁总是以相反的顺序发生。)Vehicle同样是Bicycle的基础,并且首先被初始化;由于没有 mem-initializer ,因此调用默认构造函数。

答案 1 :(得分:1)

§12.6.2定义了事物的初始化方式:

  

列表中成员初始值设定项的顺序是无关紧要的:实际的   初始化顺序如下:

     
      
  • 如果构造函数是针对派生程度最高的类的虚拟基类   按照它们在深度优先出现的顺序进行初始化   从左到右遍历基类声明(从左到右)   指的是基本说明符列表中的外观)
  •   
  • 然后,直接基地   类在它们出现时按从左到右的顺序初始化   class的基本说明符列表
  •   
  • 然后,非静态数据成员   按类定义中的声明顺序初始化。
  •   
  • 最后,   执行构造函数的主体(注意:如果初始化顺序   由成员初始化列表中的外观控制   不同的构造函数,然后析构函数将无法确保   毁灭的顺序与顺序相反   建筑)
  •   

总结您的情况(并留下虚拟功能):

  1. 按声明的继承顺序的基类
  2. 会员按照声明的顺序
  3. 因此构造函数初始值设定项列表中的顺序无效

    在第一种情况下,您错了:PersonBaby的基类,并在m_no_of_nappies之前初始化

    编辑: 你的问题

      

    Baby在其初始化列表中调用了人,因此首先出现的是Baby的初始化列表?

    [class.base.init] / 10 可能就是你要找的东西:你并没有真正“调用”基类构造函数(假设没有委托),它被调用在初始化派生对象时由编译器为您。

    编译器为您设置了一些东西,以帮助保持构造函数和析构函数的顺序正确

      

    忽略初始化器顺序的原因是保留构造函数和析构函数调用的通常FIFO顺序。允许两个构造函数使用不同的基础和成员初始化顺序将限制实现使用更动态和更昂贵的策略

    取自https://stackoverflow.com/a/24287946/1938163

    最后

      

    是对基类(编译器所做的)的隐式调用是在Bicycle的初始化列表之前还是之后完成的?

    在成员类初始化的其余部分之前,如§12.6.2。