Можете ли вы скрыть виртуальный метод в C++?
У меня есть базовый класс с виртуальной функцией.
virtual CString& Foo();
Я хочу перегрузить это в подклассе так
CString Foo();
Есть ли способ скрыть базовые классы виртуальной функции? Что-то вроде нового ключевого слова в vb.net или C#
6 ответов
Зачем кому-то делать что-то подобное? Это нарушает контракт базового класса. Если вы не хотите реализовывать подкласс, который имеет тот же интерфейс, что и базовый класс, почему вы вообще наследуете? Используйте композицию.
В C++ нет эквивалента ключевого слова C# new. Таким образом, вы не можете отменить "виртуальность" метода.
Если вы действительно хотите это сделать, вы всегда можете:
переопределить метод в подклассе как частный.
создать перегрузку. Но перегрузка должна иметь разные параметры.
Но если вы это сделаете, ИМХО что-то не так с вашим дизайном. Я бы хотел, чтобы каждый компилятор C++ воспринимал обе эти ситуации как минимум как предупреждения.
Во-первых, плохая новость заключается в том, что вы не можете переопределить или скрыть виртуальную функцию с помощью другой функции, которая отличается только типом возврата. Еще одна плохая новость: C++ дает вам достаточно веревки, чтобы повеситься во многих отношениях, о которых вы раньше не думали, и вы можете получить аналогичный эффект:
struct base {
virtual std::string const & f() {
static std::string s = "virtual";
return s;
}
};
namespace detail {
class place_holder; // no need to define it, it is just a place holder (duh)
}
struct derived : public base {
std::string f( place_holder* p = 0 ) { return "crooked"; }
};
int main() {
derived d; std::cout << d.f() << std::endl; // crooked
base& b = d; std::cout << b.f() << std::endl; // virtual
}
Хитрость в том, что, определяя метод с тем же именем и разными аргументами в производном классе, вы фактически скрываете базовый класс (если вы не добавите using base::f
в производном классе, но тогда вызов будет неоднозначным). В то же время, поскольку единственный добавленный аргумент имеет значение по умолчанию, вы можете вызывать его без аргументов, и компилятор добавит аргумент по умолчанию для вас.
Обратите внимание, что вы фактически не удаляете метод из объекта, а скорее скрываете его в производном классе. Если метод вызывается через указатель или ссылку на базовый класс, виртуальный метод все еще существует и будет вызван.
Сначала вы не можете переопределить
virtual CString& Foo()
с
CString Foo()
так как они отличаются ничем более как тип возвращаемого значения - это не скомпилируется.
Если функция с подписью
int foo();
является virtual
в базовом классе - функции с одинаковой сигнатурой в производных классах также будут виртуальными; вам не нужно указывать virtual
в производных классах.
Для получения дополнительной информации о виртуальных функциях (и больше!) Вы можете заглянуть в C++ FAQ Lite.
- ваш код не скомпилируется. Полагаю, вы это знаете, поэтому вы задали вопрос здесь.
- однако ваша задача - просто скрыть виртуальную функцию базового класса. Для этого создайте другую функцию с некоторыми другими аргументами, чем функция базового класса. Это скроет функцию базового класса.
Технически вы можете объявить метод в производном классе static:
static CString& Derived::foo()
это звучит странно, но попробуйте - компилятор будет есть и выдавать предупреждение. Переопределение будет игнорироваться, и virtual Base::foo()
будет скрыт и заменен на статический Derived::foo()
,
Но зачем вам что-то подобное?
Нет, не совсем. Даже если вы введете промежуточный класс для создания унаследованного метода private
, это не поможет, поскольку частные виртуальные методы все еще могут быть переопределены в C++.