2015-06-14 3 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

Скотт означает, что, хотя вы находитесь в конструкторе базового класса, обычные правила для виртуальной функции не работают. Поэтому, если вы находитесь в конструкторе базового класса, тогда любые виртуальные функции (этого базового класса), вызываемые внутри ctor, будут вызваны на объект, который в настоящее время строится в этом самом ctor.

Так что ваш код печатает правильный результат: foo() вызывается в ctor B не в родительском конструкторе. Если вы позвонили fooвнутриA ctor у вас было бы base напечатано.

Тем не менее поведение считается неопределенным в соответствии со стандартом:

[12.6.2/13] Функции-членов (в том числе виртуальных функций-членов, 10.3) может быть вызвана для объекта в стадии строительства. Аналогичным образом, строящийся объект может быть операндом оператора typeid (5.2.8) или dynamic_-cast (5.2.7). Однако, если эти операции выполняются в ctor-инициализаторе (или в функции, называемой непосредственно или опосредованно из ctor-инициализатора) до того, как все инициализаторы памяти для базовых классов завершены, результатом операции является undefined.

Но вы должны понимать, что «неопределенный» здесь означает, что вы можете использовать какое-то внутреннее состояние в вызываемой функции. Так как вы не знаете, поведение будет непротиворечивым, но стандарт по-прежнему считает его неопределенным. «Неопределенная» часть не имеет ничего общего с тем, что напечатано, а скорее с тем, что может быть доступно в функции-члене.

+0

Так что это не UB, не так ли? –

+0

@ St.Antario, обновлено – ixSci

+0

Интересно. Итак, если мы хотим вызвать функцию-член из ctor, мы должны объявить его статическим, верно? –

0

Что говорит Скотт Майерс, но ваша программа неверна.

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

Вызов виртуальной функции 'foo' в конструкторе B –

Смежные вопросы