С++: используйте чистую виртуальную функцию в невиртуальной функции

Я пытаюсь создать базовый класс, определяющий интерфейсы для всех производных классов.

Я хотел бы иметь функцию, которая позволяет читать файл конфигурации для этого класса, который довольно гладко работает с использованием boost::property_tree. Назовем эту функцию readConfig. Это должно быть определено в каждом производном классе, поэтому я сделал его чисто виртуальным.

Я хотел бы перегрузить функцию readConfig в базовом классе, где каждая перегруженная функция в базовом классе в конечном итоге вызывает чистую виртуальную форму, например:

class Base
{
    // ...
    void readConfig(string, string);                                 // read config from file
    virtual void readConfig(boost::property_tree::ptree, string) =0; // read config from ptree
}

void Base::readConfig(string filename, string entry)
{
     boost::property_tree::ptree pt;
     read_xml(filename, pt);
     readConfig(pt, entry);       // <= Calling pure virtual function!
}

По сути, строковая версия — это просто быстрая оболочка для чистой виртуальной формы. Когда я компилирую это, я получаю сообщение об ошибке:

no known conversion for argument 1 from std::string to boost::property_tree::ptree`

Получается, что не виртуальная функция (из Base) не распознается как доступная. Я проверил, что мое определение производного класса в порядке:

class Deriv : public Base
{
    // ...
    void readConfig(boost::property_tree::ptree, string); // implement virtual, error is on this line
}

void Deriv::readConfig( boost::property_tree::ptree pt, string entry)
{
    //...
}

обратите внимание, что я пропустил много const-корректировок, передачи по ссылке и т. д., чтобы сделать код немного более читабельным.

Что я могу сделать, чтобы исправить это? Является ли использование чистой виртуальной функции-члена в невиртуальной функции хорошей идеей?


person romeovs    schedule 05.05.2012    source источник
comment
Deriv.h, строка, в которой я объявляю функцию.   -  person romeovs    schedule 05.05.2012
comment
На самом деле вы не стали бы вызывать чистую виртуальную функцию в правильно написанной программе. Скорее, вы бы вызвали соответствующее наиболее производное переопределение, из которых есть по крайней мере одно.   -  person Kerrek SB    schedule 05.05.2012
comment
Действительно ли ваша программа вылетает из-за того, что вызывается чистая виртуальная функция?   -  person Kerrek SB    schedule 05.05.2012
comment
@KerrekSB нет, это только ворчит о конверсиях   -  person romeovs    schedule 05.05.2012
comment
где на самом деле возникает ошибка компилятора? Конечно, вы не можете рассчитывать на получение Base:readConfig(string, string) из Derived, если только вы явно не вызовете его как Base::readConfig() или не добавите using Base::readConfig; к Derived.   -  person Walter    schedule 05.05.2012
comment
пожалуйста, покажите код, который на самом деле вызывает ошибку   -  person std''OrgnlDave    schedule 05.05.2012


Ответы (2)


См. пункт часто задаваемых вопросов под названием «Что означает предупреждение: Derived::f(char) скрывает Base::f(double)?» доступны на многочисленных зеркалах FAQ, включая оригинальную английскую версию Часто задаваемые вопросы.

Перед публикацией рекомендуется проверить FAQ.

Вполне возможно, что вы также найдете полезным (хотя и не для непосредственной проблемы) пункт часто задаваемых вопросов под названием «Хорошо, но есть ли способ смоделировать такое поведение, как если бы динамическое связывание работало над объектом this в конструкторе моего базового класса?» , а также если это станет актуальным, возможно, вам также будет интересно прочитать моя статья в блоге о проблеме DBDI.

person Cheers and hth. - Alf    schedule 05.05.2012
comment
вау, вот и все.. еще один потерянный день! Странно, что этому не учат в классе... Большое спасибо! - person romeovs; 05.05.2012
comment
Я считаю, что это одна из самых сложных проблем с наследованием. Один из способов избежать этого — наследовать только от базы private и объявлять нужные члены в интерфейсе public через using (что, в свою очередь, позволяет объявлять перегруженные члены всего одним объявлением). - person Walter; 05.05.2012

Очевидная опечатка очевидна:

virtual void readConfug

вместо

virtual void readConfig

Кроме того, то, что вы реализуете, называется шаблон метода шаблона, FYI.

person Luchian Grigore    schedule 05.05.2012
comment
упс, это была опечатка при размещении на stackoverflow. Да, я знал, что это будет шаблон дизайна! - person romeovs; 05.05.2012
comment
@romeovs Я не вижу других проблем с кодом. Можете ли вы воспроизвести минимальный пример, демонстрирующий проблему - он компилируется. - person Luchian Grigore; 05.05.2012
comment
Я так и подозревал.. проблема должна быть глубже, возможно, какая-то ошибка указателя. Я бы хотел это выложить, но проблема в том, что между Base и Deriv есть еще один чистый виртуальный класс. Я посмотрю что я могу сделать. - person romeovs; 05.05.2012
comment
Вот что я сделал сейчас: я переименовал чистую виртуальную функцию во что-то другое (readConfigS). Все так хорошо работает. Это проясняет проблему? - person romeovs; 05.05.2012
comment
-1 не имеет отношения к проблеме, это должен был быть просто комментарий - person Cheers and hth. - Alf; 05.05.2012
comment
@romeovs: да, это проясняет, но проблема уже была очевидна. см. мой ответ. - person Cheers and hth. - Alf; 05.05.2012