Почему метод базового класса вызывается, если производный класс переопределяет метод?

рассмотрим следующую программу:

class Base {
public:
    virtual void foo() const {
        cout << "Base::foo()" << endl;
    }
};

class Derived : public Base {
public:
    virtual void foo() {
        cout << "Derived::foo()" << endl;
    }
};

void func(Base& obj) {
    obj.foo();
}

void main() {
    Derived d;
    func(d); // Base::foo() is printed 
}

если я удалить const С Base класс foo метод Derived::foo() называется. Я не могу понять такое поведение.

1) в чем причина такого поведения?

2) это решается во время компиляции или во время выполнения?

спасибо

5 ответов


в производном классе сигнатура функции такова:

virtual void foo(); //Derived::foo

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

virtual void foo() const; //Base::foo 

производный класс не переопределяет эту функцию, вместо этого он добавляет еще одну функцию.

чтобы исправить это:

class Derived : public Base {
public:
    virtual void foo() const {
        cout << "Derived::foo()" << endl;
    }
};

As const является частью сигнатуры функции. Поэтому вы должны упомянуть об этом, когда собираетесь переопределить Foo базы.


@давка спросил:

Итак, почему версия const выбрана над non-const? Есть ли какое-то правило или это просто первый вариант?

это потому что статический типа obj is Base, и имя функции решается на основе статического типа объекта. Base даже не есть non-const версия. Поэтому не может быть и речи о том, чтобы его отобрали или отвергли. Он не существует в Base для начала.

void func(Base& obj) {
    obj.foo();  //calls Base::foo
}

однако, если вы измените приведенный выше код на следующий:

void func(Derived & obj) {
    obj.foo();  //calls Derived:foo
}

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


С Derived::foo скрыть Base::foo, поэтому вы не можете вызвать последний, используя экземпляр Derived.

, позволяет отобразить Base::foo и сделать еще несколько экспериментов.
class Derived : public Base {
public:
    using Base::foo;         //<----------------this unhides Base::foo
    virtual void foo() {
        cout << "Derived::foo()" << endl;
    }
};

теперь в производном, обе функции (const, а также не-const версия) доступны, unhidden. Теперь несколько интересных вопросов.

поскольку теперь производные имеют обе функции unhidden, какая функция будет вызываться в каждой функции ниже?

void f(Derived& obj) {
    obj.foo(); //Which function? Base::foo or Derived::foo?
}
void g(const Derived & obj) {
    obj.foo(); //Which function? Base::foo or Derived::foo?
}

первый вызов Derived::foo который не является версией const, а второй вызовет Base::foo который является версией const. Причина проста, с const object,только const функции могут быть вызваны, но с объектами non-const оба могут быть вызваны, однако версия non-const выбрана, если она доступна.

см. онлайн-демо:http://www.ideone.com/955aY


вы не переопределяете метод, так как Derived::foo не совсем то же самое.

чтобы переопределить метод, базовая и переопределенная версии должны быть идентичны, включая const - ness.


В C++, вы можете иметь две функции с одинаковым именем и одинаковыми параметрами, где только разницей, что один const а один нет.

идея в том, что вы иногда хотите другого поведения. Например, функция access может иметь разные типы возвращаемых значений:

class MyClass
{
public:
  virtual Xxx * GetXxx();
  virtual Xxx const * GetXxx() const;
  // ....
}

вы можете переопределить эти функции по отдельности.

в вашем случае, как вы называете foo из неконст-объекта вы вызвали неконст-вариант функция. Поскольку вы переопределили const-variant, вызывается тот, который находится в базовом классе.


Что ты делаешь называется "перегрузка."В переопределении, как отметил @SLaks, подпись должна быть такой же.


const является частью сигнатуры. void foo() const функция void foo(). Вы вовсе не перекрываете. Вот почему.