2015-06-14 45 views
3

考虑方案:理解基类的初始化

#include<iostream> 
#include<vector> 

struct A 
{ 
    int a; 
    A(int) { } 
    virtual int foo(){ std::cout << "base" << std::endl; return 5; } 
}; 

struct B : A 
{ 
    int b; 
    B(): b(9), A(foo()) { } 
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; } 
}; 

B b; //prints derived 

int main(){ } 

DEMO

斯科特迈尔斯在他Effective C++说,大约是什么:

在基类构造一个派生类的对象,类型为 的对象是基类的对象。

因此,我预计base将被打印,因为我们在调用foo函数时处于基类的构造之下。我错过了什么?也许这是UB?如果是这样,请指出相关部分。

回答

3

Scott意味着当你在基类构造函数中时,通常的虚函数规则不起作用。因此,如果你在基类构造函数中,那么在ctor内调用的任何虚函数(该基类的)将被调用到当前正在构建的对象中。

因此,您的代码会输出正确的结果:foo()B的ctor中不在父构造函数中调用。如果您在A ctor内调用foo,您将会打印base

静止行为被视为根据标准未定义:

[12.6.2/13]成员函数(包括虚拟成员函数,10.3)可被称为在建的对象。同样,正在构建的对象可以是typeid运算符 (5.2.8)或dynamic_-cast(5.2.7)的操作数。 然而,如果这些操作 全部的MEM-初始化 为基类已完成之前,在构造函数初始化程序执行(或在直接从构造函数-初始化称为 或间接功能),则resultof操作 未定义

但是你应该明白这里的“未定义”意味着你可能会在被调用的函数中使用一些内部状态。既然你不这样做的行为是一致的,但标准仍然认为它没有定义。 “未定义”部分与打印内容无关,而与成员函数中可访问的内容无关。

+0

所以它不是UB,不是吗? –

+0

@ St.Antario,更新 – ixSci

+0

有趣。所以,如果我们想从ctor调用成员函数,我们应该声明它是静态的,对吗? –

0

Scott Mayers说的是正确的,但是你的程序是错误的。

B(): b(9), A(foo()) { } 

这种说法绝对是错误的:

在派生类的构造函数初始化列表,你应该首先初始化派生类成员对象之前调用基类的构造函数。在实例化对象之前不能调用非静态成员函数。

#include<iostream> 
#include<vector> 

struct A 
{ 
    int a; 
    A(int) { std::cout<<"base constructed\n"; } 
    virtual int foo(){ std::cout << "base" << std::endl; return 5; } 
}; 

struct B : A 
{ 
    int b; 
    B(): A(6), b(9) { std::cout<<"derived constructed"; } 
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; } 
}; 



int main(){ 
    B b; //prints derived 
    } 

O/P 

base constructed 

derived constructed 
1

只是不使用构造函数中的虚函数 - 第9项

+0

它与这个问题有什么关系? –

+0

在B的构造函数中调用一个虚函数'foo' –