Перевизначення невіртуальних методів


84

Припустимо, цей сценарій у Visual C ++ 2010:

#include <iostream>
#include <conio.h>

using namespace std;

class Base
{
public:
    int b;
    void Display()
    {
        cout<<"Base: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Base: Virtual display."<<endl;
    };
};

class Derived : public Base
{
public:
    int d;
    void Display()
    {
        cout<<"Derived: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Derived: Virtual display."<<endl;
    };
};

int main()
{
    Base ba;
    Derived de;

    ba.Display();
    ba.vDisplay();
    de.Display();
    de.vDisplay();

    _getch();
    return 0;
};

Теоретично, результат цього маленького додатка повинен бути:

  • Основа: невіртуальний дисплей.
  • Основа: Віртуальний дисплей.
  • Основа: невіртуальний дисплей.
  • Виведено: віртуальний дисплей.

оскільки метод Display базового класу не є віртуальним методом, тому похідний клас не повинен мати можливість його замінити. Правда?

Проблема в тому, що коли я запускаю програму, вона друкує це:

  • Основа: невіртуальний дисплей.
  • Основа: Віртуальний дисплей.
  • Похідне: невіртуальний дисплей.
  • Виведено: віртуальний дисплей.

Отже, або я не розумів поняття віртуальних методів, або щось дивне відбувається у Visual C ++.

Чи може хтось допомогти мені з поясненням?


ви б абсолютно мали Base: Невіртуальний дисплей. при зміні лінії на de.Base::Display().
v.oddou

Відповіді:


128

Так, ви трохи нерозумієте.

У цьому випадку однойменний метод у похідному класі приховає батьківський метод. Ви могли б уявити, що якби це було не так, намагаючись створити метод з тим самим іменем, що і невіртуальний метод базового класу, слід викликати помилку. Це дозволено, і це не проблема - і якщо ви викликаєте метод безпосередньо, як ви це робили, це буде називатися нормально.

Але, будучи невіртуальними, механізми пошуку методів C ++, що дозволяють поліморфізм, використовувати не будуть. Так, наприклад, якщо ви створили екземпляр похідного класу, але викликали метод 'Display' через вказівник на базовий клас, буде викликаний метод base, тоді як для 'vDisplay' буде викликаний похідний метод.

Наприклад, спробуйте додати такі рядки:

Base *b = &ba;
b->Display();
b->vDisplay();
b = &de;
b->Display();
b->vDisplay();

... і спостерігайте за результатами, як очікувалося:

Основа: невіртуальний дисплей.
Основа: Віртуальний дисплей.
Основа: невіртуальний дисплей.
Виведено: віртуальний дисплей.


Привіт @ sje397, дякую за відповідь. Чи можете ви написати приклад виклику методу, як ви вже сказали, через вказівник на базовий клас? Дякую!
Лейф Лазар,

також, як я вже сказав, ви також можете викликати (невіртуальний) базовий метод із похідного екземпляра, використовуючи синтаксис роздільної здатності.
v.oddou

1
Тож, щоб бути впевненим, я можу визначити метод у базовому класі та замінити його у похідному класі, незалежно від того, оголошувати його віртуальним чи ні. Єдина відмінність полягає в тому, що якщо базовий вказівник вказує на похідний об’єкт класу, тоді виклик цього методу буде викликати метод базового класу, якщо він не віртуальний, і метод похідного класу, якщо він віртуальний. Це так? Чи є якась інша різниця?
SexyBeast

@Cupidvogel Так, це правильно. Оголошення "віртуальним" означає, що C ++ буде використовувати механізми для підтримки поліморфізму та перевіряти, чи існує більш похідна версія методу, коли ви телефонуєте через вказівник базового класу. Я не можу придумати жодної іншої різниці.
sje397

Чи достатньо змінити лише файл заголовка? Або джерело має бути скомпільоване з ключовим словом "віртуальне"?
Пол Нопф,

14

Так, ви трохи неправильно зрозуміли:

Чисті віртуальні функції:

virtual void fun1()=0 -> має бути замінено у похідному класі

Віртуальні функції:

virtual void fun2() -> можна замінити

Звичайні функції:

void fun3() -> не перевизначати

Для того, щоб досягти поліморфізму виконання, вам потрібно замінити віртуальні функції в c ++


5

Я думаю, що, можливо, було б також краще розглянути це в контексті статичного та динамічного прив'язки.

Якщо метод невіртуальний (на відміну від Java це вже за замовчуванням у C ++), тоді метод прив'язується до свого абонента під час компіляції, що неможливо дізнатись про фактичний об'єкт, який буде вказаний під час виконання. Отже, тип змінної - це все, що має значення, що є "базою".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.