Переопределение функций в C++ работает без "виртуального"

У меня есть класс, который содержит некоторые функции (ни одна не является виртуальной), и еще 2 класса публично наследуют этот класс. В обоих подклассах я переопределяю одну и ту же функцию базового класса.

После создания объектов всех трех классов в main (расположенных в одном и том же файле) я вызываю исходную функцию с объектом baseclass и переопределенные функции с объектами производного класса.

Я ожидал, что все 3 вызова функций запустят исходную функцию из базового класса (так как я нигде не использовал "виртуальный" в коде), но я фактически заставляю каждую версию этой функции работать в соответствии с классом, в котором она была определены (3 разных варианта).

У меня есть классы Base & Derived следующим образом:

struct Base
{
   void foo();
};

struct Derived : Base
{
   void foo();
};

в основном:

int main()
{
   Derived d;
   d.foo();
}

Я думал, что d.foo() должен запускать Base::foo(), если не используется 'virtual'.

3 ответа

Решение

Это не "переопределение"... и не должно быть.

struct Base
{
   void foo();
};

struct Derived : Base
{
   void foo();
};

int main()
{
   Derived d;
   d.foo();
}

Если я вас правильно понимаю, то вы ожидали, что это исполнится Base::foo()потому что функции не являются виртуальными и, следовательно, одна не переопределяет другую.

Но здесь вам не нужна виртуальная диспетчеризация: правила наследования просто утверждают, что вы получите правильную функцию для типа объекта, на котором вы ее выполняете.

Когда вам нужна виртуальная диспетчеризация / переопределение, это немного другой случай: это когда вы используете косвенное обращение:

int main()
{
   Base* ptr = new Derived();
   ptr->foo();
   delete ptr;
}

В приведенном фрагменте результат будет Base::foo() называется, потому что выражение ptr->foo() не знает что *ptr действительно Derived, Все, что он знает, это то, что ptr это Base*,

Это где добавление virtual (и, делая так, одна функция переопределяет другую), происходит волшебство.

Вы не можете переопределить то, что не является виртуальным. Не виртуальные функции-члены отправляются статически в зависимости от типа объекта экземпляра.

Вы можете обмануть, "переопределив" функцию, сделав ее встроенной функцией, вызывающей что-то косвенно. Нечто подобное (в C++ 03)

 class Foo;
 typedef int foo_sig_t (Foo&, std::string&);
 class Foo {
    foo_sig_t *funptr;
 public:
    int do_fun(std::string&s) { return funptr(*this,s); }
    Foo (foo_sig_t* fun): funptr(fun) {};
    ~Foo () { funptr= NULL; };
    // etc
 };

 class Bar : public Foo {
    static int barfun(Bar&, std::string& s) { 
       std::cout << s << std::endl;
       return (int) s.size();
    };
  public: 
    Bar () : Foo(reinterpret_cast<foo_sig_t*>)(&barfun)) {};
     // etc...
  };

и позже:

  Bar b;
  int x=b.do_fun("hello");

Официально это не перегружает виртуальную функцию, но выглядит очень близко к ней. Однако по моему выше Foo пример каждого Foo экземпляр имеет свой funptr , который не обязательно является общим для класса. Но все Bar экземпляры делят то же самое funptr указывая на то же самое barfun ,

Кстати, используя лямбда- анонимные функции C++ 11 (внутренне реализованные как замыкания), это было бы проще и короче.

Конечно, виртуальные функции в действительности фактически реализуются с помощью аналогичного механизма: объектов (с некоторыми virtual материал) неявно начать со скрытого поля (возможно, "по имени" _vptr) предоставление vtable (или таблицы виртуальных методов).

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