Почему метод базового класса вызывается, если производный класс переопределяет метод?
рассмотрим следующую программу:
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()
. Вы вовсе не перекрываете. Вот почему.