2015-06-11 5 views
10

Я попытался «восстановить» пример в this answer, чтобы продемонстрировать, как можно вызывать чистую виртуальную функцию.Почему это не вызов чистой виртуальной функции?

#include <iostream> 
using namespace std; 

class A 
{ 
    int id; 
public: 
    A(int i): id(i) {} 
    int callFoo() { return foo(); } 
    virtual int foo() = 0; 
}; 

class B: public A 
{ 
public: 
    B(): A(callFoo()) {} 
    int foo() { return 3; } 
}; 

int main() { 
    B b; // <-- this should call a pure virtual function 
    cout << b.callFoo() << endl; 
    return 0; 
} 

Но я не получаю сообщение об ошибке выполнения here (with C++ 4.9.2), но выход 3. Я попробовал то же самое с Borland C++ 5.6.4, но я получаю сообщение о нарушении прав доступа. Я думаю, что foo() должен быть чистым виртуальным в вызове конструктора базового класса.

Кто не так? Должен ли я попробовать больше компиляторов? Я прав в своем понимании виртуальных функций?

+4

Я бы не использовал результаты теста с Borland C++, чтобы убедиться, что код кода действителен и/или соответствует стандарту;) – CoryKramer

+0

@CoryKramer Виртуальные функции используются уже несколько десятилетий. – Wolf

+1

Мне это известно, я делал дерзкое замечание о том, что кто-то все еще использует Borland IDE – CoryKramer

ответ

14

Ваш код имеет неопределенное поведение: UB вызывает функцию-член на объекте (даже не виртуальном), прежде чем все базовые классы будут инициализированы. C++ 14 (n4140) 12.6.2/14, focus mine:

Функции объекта (включая функции виртуальных элементов, 10.3) можно вызвать для объекта, находящегося в процессе строительства. Аналогичным образом, объект, находящийся в процессе строительства, может быть операндом оператора typeid (5.2.8) или dynamic_cast (5.2.7). Однако, если эти операции выполняются в CTOR-инициализаторе (или в функции вызывается непосредственно или косвенно из CTOR-инициализатора) перед всеми MEM-инициализаторами для базовых классов закончили, в результате операции не определено. ...

т е р-инициализатор является весь список следующих :. mem-initializer является одним из элементов этого списка.

+0

Правда, и это правило UB является обоснованным. Но это только движет головной болью - никакие компиляторы и статические проверки кода не обнаружат всех ошибок. Я был шокирован тем, что такие простые встроенные случаи не сразу отвергались. – Wolf

+0

@Wolf: компилятор [не требуется выполнять полный анализ потока управления] (http://blogs.msdn.com/b/oldnewthing/archive/2013/10/11/10455907.aspx), поскольку это может не удастся сделать статически во время компиляции. – Kevin

+0

@Kevin Реализация чистых виртуальных функций, чтобы избавиться от * чистой виртуальной функции, называемой * сообщениями, кажется мне неправильным, или она предназначена именно для этой цели? – Wolf

5

Оператор B b; вызывает конструктор по умолчанию B.

При строительстве B ничего не относящегося к B не создано до A.

Таким образом, при попытке вызвать callFoo() поведение не определено, так как вы не можете полагаться на таблицу v для таблицы B.

В целом: поведение вызова чистой виртуальной функции при построении абстрактного класса не определено.

+0

Я догадался, что это будет так, потому что. Так что, кажется, я немного ленился с предложением «ремонта». – Wolf

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