2013-05-15 4 views
2

У меня есть абстрактный базовый класс Foo с абстрактным методом под названием bar. Я звоню bar из конструктора Foo. Я ожидаю, что подклассы, чтобы переопределить bar (оно абстрактно в конце концов), а затем Foo вызвать преодолено bar на каждом экземпляре:Ошибка компоновщика, не объявляя абстрактного метода в абстрактном базовом классе?

class Foo 
{ 
    public: 
     Foo() { bar(); } 
     virtual void bar() = 0; 
} 

Однако я получаю сообщение об ошибке:

foo.obj:-1: error: LNK2019: unresolved external symbol "protected: virtual void __cdecl Foo::bar(void)" ([email protected]@@MEAAXXZ) referenced in function "public: __cdecl Foo::Foo(void)" ([email protected]@[email protected]) 

Такие ошибки компоновщика обычно означают, что я что-то определил, но не объявил об этом (или наоборот), и это, похоже, повторится. Все работает просто отлично, если добавить определение для bar так:

void Foo::bar() {} 

Это предназначена ошибка, или это ошибка в линкере? Если это намеренно, почему? Я не понимаю, почему я не могу назвать абстрактный метод конструктором? Единственная причина, по которой я думаю, заключается в том, что конструктор базового класса получает вызов до того, как будут определены методы подкласса, но я все же не думаю, что должен получить эту ошибку?

Я использую Qt Creator 2.7.0 - Based on Qt 5.0.2 (32 bit)

ответ

3

Внутри конструктора, динамический тип *this является Foo, и нет никакой определенной функции Foo::bar. Не вызывайте виртуальные функции в конструкторах. (Если вы не знаете, что делаете, и в этом случае вы знаете, как дать определение.)

+0

Как я думал ... Есть ли причина, по которой они добавили/удалили эту функцию? Я уже определил, что 'Foo' имеет абстрактный метод, называемый' bar', почему я не могу его вызвать из конструктора, так как он будет существовать в любом случае? –

+0

@MarkusMeskanen: Боюсь, что ваш вопрос не имеет смысла. Подумайте немного сложнее о том, как работают объекты C++ (особенно конструкция и разрушение при наличии базовых классов) ... нет полезного понятия «функция», как вы утверждаете. –

+0

Извините за недостаток знаний ... Я не могу создать объект типа 'Foo', поэтому у меня должен быть подкласс, который имеет в нем' bar', назовем его 'SubFoo'.Теперь скажем, что 'SubFoo * sf = new SubFoo;', очевидно, будет объектом 'SubFoo', который имеет' bar'. Поэтому я не могу понять, почему я не могу просто вызвать 'bar' из конструктора Foo, поскольку он всегда будет определен. –

1

Ваша проблема проста и очень сложна: конструктор Foo работает до того, что производного класса. Таким образом, виртуальная функция установлена ​​на то, что Foo устанавливает ее, - которая не реализована.

Вам нужно подождать до завершения строительства, прежде чем вы сможете использовать виртуальную функцию так, как вы ожидаете, и все записи vtable устанавливаются в их наиболее производные версии.

Хотя было бы возможно разбить конструкцию на три части (сначала vtable обновляется каждым конструктором, чем все инициализаторы, и, наконец, все функции конструктора выполняются), комитет по стандартам C++ принял решение об этом.

Есть и преимущества, как это делается, кроме простоты для сборщиков компилятора: если бы вы могли получить доступ к более производным членам класса (вызывая переопределенную функцию virtual), они не были бы инициализированы (или даже построены), тем не менее, поэтому любая функция более производного класса не должна касаться каких-либо нетривиальных элементов данных, что усложняет все, даже просто говоря, что вы не должны называть виртуальные функции во время построения.

+0

Чтобы быть педантичным, функции переопределения выполняются во время разработки, а не во время выполнения ... говоря " еще нет "немного странно. –

+0

@ KerrekSB Вы правы; Я немного изменил формулировку, чтобы лучше представить это. –

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