C ++ Родительский класс, вызывающий дочернюю виртуальную функцию
-
04-07-2019 - |
Вопрос
Я хочу, чтобы чисто виртуальный родительский класс вызывал дочернюю реализацию функции следующим образом:
class parent
{
public:
void Read() { //read stuff }
virtual void Process() = 0;
parent()
{
Read();
Process();
}
}
class child : public parent
{
public:
virtual void Process() { //process stuff }
child() : parent() { }
}
int main()
{
child c;
}
Это должно работать, но я получаю несвязанную ошибку: / Это использует VC ++ 2k3
Или это не должно работать, я не прав?
Решение
Название следующей статьи говорит само за себя: Никогда не вызывайте виртуальные функции во время строительства или разрушения . р>
Другие советы
В качестве альтернативы, создайте фабричный метод для создания объектов и сделайте конструкторы частными, тогда фабричный метод может инициализировать объект после построения.
Будет работать в целом, но не для вызовов внутри конструктора чисто виртуального базового класса. В то время как базовый класс создан, переопределение подкласса не существует, поэтому его нельзя вызвать. Пока вы вызываете его, как только весь объект создан, он должен работать.
Это потому, что ваш вызов в конструкторе. Производный класс не будет действительным до тех пор, пока конструктор не завершит работу, поэтому вы, компилятор, правы в этом.
Есть два решения:
<Ол>class parent
{
public:
void Read() { //read stuff }
virtual void Process() { }
parent()
{
Read();
Process();
}
}
Еще на один шаг вы можете просто ввести какую-то функцию вроде
class parent
{
public:
void initialize() {
read();
process();
}
}
Вам нужно обернуть объект, который вызывает виртуальный метод, после того, как объект полностью построен:
class parent
{
public:
void Read() { /*read stuff*/ }
virtual void Process() = 0;
parent()
{
Read();
}
};
class child: public parent
{
public:
virtual void Process() { /*process stuff*/ }
child() : parent() { }
};
template<typename T>
class Processor
{
public:
Processor()
:processorObj() // Pass on any args here
{
processorObj.Process();
}
private:
T processorObj;
};
int main()
{
Processor<child> c;
}
Проблема поверхностного заключается в том, что вы вызываете виртуальную функцию, которая еще не известна (объекты создаются от родителя к ребенку, как и таблицы). Ваш компилятор предупреждал вас об этом.
Насколько я понимаю, существенная проблема заключается в том, что вы пытаетесь повторно использовать функциональность путем наследования. Это почти всегда плохая идея. Вопрос дизайна, так сказать:)
По сути, вы пытаетесь создать экземпляр шаблона Template Method, чтобы отделить что от когда : сначала прочитайте некоторые данные (каким-то образом), затем обработайте их (в каким-то образом).
Вероятно, это будет намного лучше работать с агрегацией: передайте функцию Processing методу Template, который будет вызван в нужное время. Может быть, вы можете сделать то же самое для функции чтения.
Агрегирование может быть выполнено двумя способами:
<Ол>Пример 1: привязка во время выполнения
class Data {};
class IReader { public: virtual Data read() = 0; };
class IProcessor { public: virtual void process( Data& d) = 0; };
class ReadNProcess {
public:
ReadNProcess( IReader& reader, IProcessor processor ){
processor.process( reader.read() );
}
};
Пример 2: привязка времени компиляции
template< typename Reader, typename Writer > // definitely could use concepts here :)
class ReadNProcess {
public:
ReadNProcess( Reader& r, Processor& p ) {
p.process( r.read() );
}
};