Почему метод базового класса вызывается, если производный класс переопределяет метод? - PullRequest
6 голосов
/ 10 июня 2011

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

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 ]

7 голосов
/ 10 июня 2011

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

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

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

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

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

Итак, исправление таково:

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

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


@ Дэвка спросил:

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

Это потому, что static тип obj равен Base, а имя функции определяется на основе статического типа объекта. Base даже не имеет неконстантной версии. Таким образом, нет сомнений в том, что он был выбран или отклонен. Его не существует в Base для начала.

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

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

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

Теперь будет выбрана неконстантная версия, потому что 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;
    }
};

Теперь в Derived обе функции (как константной, так и неконстантной версии) доступны, невидимые. Теперь несколько интересных вопросов.

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

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, который является неконстантной версией, а второй вызовет Base::foo, который является константной версией. Причина проста: с const-объектом можно вызывать только функции * * const , но с неконстантными объектами можно вызывать обе функции, однако выбирается неконстантная версия, если она доступна .

Смотрите онлайн демо: http://www.ideone.com/955aY

3 голосов
/ 10 июня 2011

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

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

1 голос
/ 10 июня 2011

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

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

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

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

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

0 голосов
/ 10 июня 2011

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

0 голосов
/ 10 июня 2011

То, что вы делаете, называется «перегрузкой».В переопределении, как указал @SLaks, подпись должна быть одинаковой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...