【问题标题】:Inheritance, why do two constructors get called for base class, C++继承,为什么要为基类调用两个构造函数,C++
【发布时间】:2015-12-06 23:09:53
【问题描述】:

我正在尝试在我的书中做一个作业,但我不理解输出。当程序中的主要代码运行时,输出为:

B::B(3)

B::B() //为什么会这样输出

B::B(-3)

D::D(3)

它首先调用B::B(int n) {},然后调用B::B() {},然后是我得到的接下来的两行。所以程序调用了第一个程序,因为它被声明为函数中的类 A 的构造函数并且它必须赋值,我没有得到的是输出第 2 行,为什么 B::B() {} 甚至被调用?它作为构造函数被调用,但不应该只调用带参数的构造函数?

class B {
public:
    B(); //why is this  called?
    B(int n);
};

// Definitions of B
B::B() { 
    cout << "B::B()\n";
}
B::B(int n) {
    cout << "B::B(" << n << ")\n";
}

class D : public B {
public:
    D();
    D(int n);
private:
    B b;
};

// Definitions of D
D::D() {
    cout << "D::D()\n";
}
D::D(int n) : B(n) {
    b = B(-n);
    cout << "D::D("<< n <<")\n";
}

int main(int argc, const char * argv[]) {
    // insert code here...

    D d(3);
    return 0;    
}

【问题讨论】:

    标签: c++ class inheritance constructor


    【解决方案1】:

    首先调用基类构造函数:B::B(3)
    然后调用b 字段的构造函数:B::B()
    然后执行派生的构造函数主体(所有字段都构造完成之后)。
    D 的构造函数首先在b = B(-n); 行上构造另一个B(因此打印了B::B(-3) ),然后打印D::D(3)

    【讨论】:

    • 但是为什么要调用 b 字段的构造函数呢? B::B(3) 不是 b 字段的构造函数吗?
    • 这是基类的构造函数。派生类隐含地包含一个基类对象。如果您只需要一个B,那么您的B b; 是多余的。
    • @user2076774 不,b 字段是完全正常的字段,与基类没有任何关系(只是巧合的是同类型)。
    【解决方案2】:

    您的类D 包含两个B 类型的对象。

    1. 来自继承的隐式B
    2. 显式B 成员名为b

    你可以重写构造函数

    D::D(int n) : B(n)
    {
        b = B(-n);
        cout << "D::D("<< n <<")\n";
    }
    

    喜欢

    D::D(int n) : B(n), b()
    {
        b = B(-n);
        cout << "D::D("<< n <<")\n";
    }
    

    B::B() 的来源很明显。因此,您的输出很容易解释:

    B::B(3) // base constructor of `D`
    B::B() // default construction of `b` member
    B::B(-3) // explicit construction inside D::D(int)
    D::D(3) // D::D(int)
    

    如果你写

    D::D(int n) : B(n), b(-n)
    {
        cout << "D::D("<< n <<")\n";
    }
    

    相反,只有

    B::B(3)
    B::B(-3)
    D::D(3)
    

    左。

    解决评论:

    想象D 的样子:

    class D : public B
    {
    public:
      D(int);
    };
    

    带有构造函数定义

    D::D(int v) { std::cout << "foo with " << v; }
    

    然后,代码

    D d(3);
    

    打印

    B::B()
    foo with 3
    

    只要B 是默认可构造的,它就完全有效。

    如果B 缺少默认构造函数,您会收到编译器错误,并且您需要将D 的定义重写为

    D::D(int v) : B(3) { std::cout << "foo with " << v; }
    

    不是构造函数初始化列表一部分的每个直接基类或成员都是默认构造的(在类类型的情况下)或在基本类型的情况下保持未初始化。

    【讨论】:

    • 好的,所以当派生类调用构造函数时,它是否总是需要基类的构造函数?假设 D::D(int n) : B(n) {} 只是 D::D(int n) {} 并打印出一个字符串而不是调用一个对象,因为 'b' 数据字段会发生错误对象不包括在内?
    • 所以即使我没有在我的主代码中实例化 b 类的对象,B 类的默认构造函数也会运行?
    • @user2076774:你看我最后一句话了吗?仅当您从初始值设定项列表中保留成员或基类型时,才会调用默认构造函数。
    • 对不起,这个概念对我来说很奇怪。在您为编辑编写的第二个示例中,如果 b 是私有变量,尽管它没有在 D::D(int v) { std::cout
    • @user2076774 b 是否在 D::D(int v) { std::cout &lt;&lt; "foo with " &lt;&lt; v; } 中显式初始化? .... 不,因此它被隐式初始化(​​这是默认构造)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    • 2014-07-25
    • 1970-01-01
    相关资源
    最近更新 更多