Виртуальная функция
У меня есть вопрос относительно виртуальной функции C++.
Производный класс (DerivedAlgo
) реализует виртуальную функцию (BaseAlgo::process
), но немного меняется в сигнатуре функции, используя DerivedData
введите в качестве входного параметра вместо BaseData.
Несмотря на чисто абстрактную функцию в базовом классе, я все еще могу создать экземпляр DerivedAlgo
класс и компилятор вообще не жалуется.
Мне просто интересно, кто-нибудь знает любое правило C++, которое может объяснить следующий код.
class BaseData{};
class DerivedData: public BaseData{};
class BaseAlgo{
public:
virtual void process(BaseData& data) = 0;
};
class DerivedAlgo: BaseAlgo{
public:
virtual void process(DerivedData& data){
std::cout << "hello world!" << std::endl;
}
};
В моем другом примере кода я определяю process(DerivedData&)
а также process(BaseData&)
,
Компилятор все еще может работать без всякой двусмысленности.
class DerivedAlgo{
public:
virtual void process(DerivedData& data){...}
virtual void process(BaseData& data){...}
};
BaseData baseData;
DerivedData derivedData;
derivedAlgo.process(baseData);
derivedAlgo.process(derivedData);
Я действительно ценю любой ваш вклад. Спасибо!
3 ответа
Ваш первый пример не компилируется на g++ 4.2. Для справки я сделал это:
#include <iostream>
class BaseData{};
class DerivedData: public BaseData{};
class BaseAlgo{
public:
virtual void process(BaseData& data) = 0;
};
class DerivedAlgo: BaseAlgo{
public:
virtual void process(DerivedData& data){
std::cout << "hello world!" << std::endl;
}
};
int main()
{
DerivedAlgo da;
return 0;
}
И получил это:
error: cannot declare variable 'da' to be of abstract type 'DerivedAlgo'
note: because the following virtual functions are pure within 'DerivedAlgo'
note: virtual void BaseAlgo::process(BaseData&)
Если что-то вроде моего примера скомпилировалось, то вы почти наверняка имеете глючный компилятор.
Ваш второй пример будет скомпилирован, потому что он переопределяет абстрактную функцию. Первый вызов вызовет базовую версию данных, а второй вызов вызовет производную версию процесса.
Обратите внимание, что в первом примере вы скрываете process(base)
версия функции, не перекрывая ее вообще. Вы не должны изменять сигнатуру функции при переопределении виртуальных функций.
Вы уверены, что не получаете ошибку компилятора?
Когда я компилирую это (в MSVC9):
#include <cstdlib>
#include <memory>
#include <iostream>
class BaseData{};
class DerivedData: public BaseData{};
class BaseAlgo{
public:
virtual void process(BaseData& data) = 0;
};
class DerivedAlgo: BaseAlgo{
public:
virtual void process(DerivedData& data){
data;
std::cout << "hello world!" << std::endl;
}
};
int main()
{
DerivedAlgo da;
}
Я получаю ожидаемую ошибку:
1>.\main.cpp(21) : error C2259: 'DerivedAlgo' : cannot instantiate abstract class
1> due to following members:
1> 'void BaseAlgo::process(BaseData &)' : is abstract
1> .\main.cpp(9) : see declaration of 'BaseAlgo::process'
Это потому, что вы не можете переопределить виртуальные объекты с другими списками параметров:
10.3 Виртуальные функции
2/ Если виртуальная функция-член vf объявлена в классе Base и в классе Derived, полученном прямо или косвенно из Base, объявляется функция-член vf с тем же именем и тем же списком параметров, что и Base:: vf, тогда Derived::vf также является виртуальным (независимо от того, объявлено оно или нет) и переопределяет97) Base::vf.
Единственный выход при объявлении переопределений в производном классе - это использование ковариантных возвращаемых типов.
Возможно, ваш компилятор не проверил ваш код полностью, если вы никогда не вызывали производный класс.
Проблема в том, что C++ не поддерживает двойную диспетчеризацию. Вам нужно использовать шаблон Visitor в вашем классе BaseAlgo.