Можете ли вы скрыть виртуальный метод в 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.

  1. ваш код не скомпилируется. Полагаю, вы это знаете, поэтому вы задали вопрос здесь.
  2. однако ваша задача - просто скрыть виртуальную функцию базового класса. Для этого создайте другую функцию с некоторыми другими аргументами, чем функция базового класса. Это скроет функцию базового класса.

Технически вы можете объявить метод в производном классе static:

static CString& Derived::foo()

это звучит странно, но попробуйте - компилятор будет есть и выдавать предупреждение. Переопределение будет игнорироваться, и virtual Base::foo() будет скрыт и заменен на статический Derived::foo(),

Но зачем вам что-то подобное?

Нет, не совсем. Даже если вы введете промежуточный класс для создания унаследованного метода private, это не поможет, поскольку частные виртуальные методы все еще могут быть переопределены в C++.

Другие вопросы по тегам