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