вызов чисто виртуальной функции из конструктора базового класса
у меня есть базовый класс MyBase, который содержит чистую виртуальную функцию:
void PrintStartMessage() = 0
Я хочу, чтобы каждый производный класс должен назвать его в конструктор
затем я поместил его в базовый класс (MyBase
) конструктор
class MyBase
{
public:
virtual void PrintStartMessage() =0;
MyBase()
{
PrintStartMessage();
}
};
class Derived:public MyBase
{
public:
void PrintStartMessage(){
}
};
void main()
{
Derived derived;
}
но я получаю ошибка компоновщика.
this is error message :
1>------ Build started: Project: s1, Configuration: Debug Win32 ------
1>Compiling...
1>s1.cpp
1>Linking...
1>s1.obj : error LNK2019: unresolved external symbol "public: virtual void __thiscall MyBase::PrintStartMessage(void)" (?PrintStartMessage@MyBase@@UAEXXZ) referenced in function "public: __thiscall MyBase::MyBase(void)" (??0MyBase@@QAE@XZ)
1>C:UsersShmuelianDocumentsVisual Studio 2008Projectss1Debugs1.exe : fatal error LNK1120: 1 unresolved externals
1>s1 - 2 error(s), 0 warning(s)
Я хочу заставить все производные классы...
A- implement it
B- call it in their constructor
как я должен это делать?
6 ответов
есть много статей, которые объясняют, почему вы не должны вызывать виртуальные функции в конструкторе и деструкторе в C++. Взгляните здесь и здесь для подробностей, что происходит за кулисами во время таких вызовов.
короче говоря, объекты строятся от основания до производного. Поэтому при попытке вызвать виртуальную функцию из конструктора базового класса переопределение из производных классов еще не произошло, поскольку производные конструкторы еще не звонили.
самое близкое, что вы можете сделать, это сначала полностью построить свой объект, а затем вызвать метод после:
template <typename T>
T construct_and_print()
{
T obj;
obj.PrintStartMessage();
return obj;
}
int main()
{
Derived derived = construct_and_print<Derived>();
}
попытка вызвать чистый абстрактный метод из производного, пока этот объект все еще строится, небезопасна. Это все равно что заправлять машину, но она все еще на конвейере, а бензобак еще не установлен. Я имею в виду, какого черта ты ждешь?
вы не можете сделать это так, как Вы себе представляете, потому что вы не можете назвать производным виртуальные функции из конструктора базового класса объект еще не производного типа. Но тебе не нужно этого делать.
вызов PrintStartMessage после построения MyBase
давайте предположим, что вы хотите сделать что-то вроде этого:
class MyBase {
public:
virtual void PrintStartMessage() = 0;
MyBase() {
printf("Doing MyBase initialization...\n");
PrintStartMessage(); // ⚠ UB: pure virtual function call ⚠
}
};
class Derived : public MyBase {
public:
virtual void PrintStartMessage() { printf("Starting Derived!!!\n"); }
};
желаемая трассировка выполнения будет:
Doing MyBase initialization...
Starting Derived!!!
но для этого и существуют конструкторы! Просто отбросьте виртуальную функцию и сделайте конструктор производного, чтобы выполнить эту работу!
class MyBase {
public:
MyBase() { printf("Doing MyBase initialization...\n"); }
};
class Derived : public MyBase {
public:
Derived() { printf("Starting Derived!!!\n"); }
};
выход, хорошо, чего мы ожидали бы:
Doing MyBase initialization...
Starting Derived!!!
это не применяет производные классы для явной реализации PrintStartMessage
функциональность, хотя. Но с другой стороны, подумайте дважды, нужно ли это вообще, поскольку в противном случае они всегда могут обеспечить пустую реализацию.
вызов PrintStartMessage перед MyBase строительство
как сказано выше, если вы не называть PrintStartMessage
до Derived
был конструктор, вы не можете этого сделать, потому что еще нет
вы не должны называть virtual
функция в конструкторе. период. Вам придется найти обходной путь, например, сделать PrintStartMessage
неvirtual
и явно помещая вызов в каждый конструктор.
Если PrintStartMessage() не является чистой виртуальной функцией, а обычной виртуальной функцией, компилятор не будет жаловаться на это. Однако вам все равно придется выяснить, почему производная версия PrintStartMessage() не вызывается.
поскольку производный класс вызывает конструктор базового класса перед его собственным конструктором, производный класс ведет себя как базовый класс и поэтому вызывает функцию базового класса.
Я знаю, что это старый вопрос, но я столкнулся с тем же вопросом во время работы над моей программой.
Если ваша цель-уменьшить дублирование кода, имея базовый класс обрабатывать общий код инициализации, требуя, чтобы производные классы указывали уникальный для них код в чистом виртуальном методе, это то, что я решил.
#include <iostream>
class MyBase
{
public:
virtual void UniqueCode() = 0;
MyBase() {};
void init(MyBase & other)
{
std::cout << "Shared Code before the unique code" << std::endl;
other.UniqueCode();
std::cout << "Shared Code after the unique code" << std::endl << std::endl;
}
};
class FirstDerived : public MyBase
{
public:
FirstDerived() : MyBase() { init(*this); };
void UniqueCode()
{
std::cout << "Code Unique to First Derived Class" << std::endl;
}
private:
using MyBase::init;
};
class SecondDerived : public MyBase
{
public:
SecondDerived() : MyBase() { init(*this); };
void UniqueCode()
{
std::cout << "Code Unique to Second Derived Class" << std::endl;
}
private:
using MyBase::init;
};
int main()
{
FirstDerived first;
SecondDerived second;
}
выход:
Shared Code before the unique code
Code Unique to First Derived Class
Shared Code after the unique code
Shared Code before the unique code
Code Unique to Second Derived Class
Shared Code after the unique code