Чи ВСІ віртуальні функції потрібно реалізовувати у похідних класах?


91

Це може здатися простим запитанням, але я не можу знайти відповідь ніде більше.

Припустимо, у мене є таке:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Чи нормально, що похідний клас не реалізує функцію bar ()? Що робити, якщо не ВСІ мої похідні класи потребують функції bar (), але деякі потрібні. Чи всі віртуальні функції абстрактного базового класу повинні бути реалізовані у похідних класах, чи лише ті, які є чисто віртуальними? Дякую

Відповіді:


82

Похідні класи не повинні самі реалізовувати всі віртуальні функції. Їм потрібно лише впровадити чисті . 1 Це означає, що Derivedклас у питанні правильний. Він успадковує в barреалізації від свого предка класу Abstract. (Це передбачає, що Abstract::barдесь реалізовано. Код у питанні декларує метод, але не визначає його. Ви можете визначити його вбудовано, як показує відповідь Тренкі , або ви можете визначити його окремо.)


1 І навіть тоді, лише якщо похідний клас буде інстанційований . Якщо похідний клас не створюється безпосередньо, а існує лише як базовий клас з більш похідних класів, то саме ті класи відповідають за реалізацію всіх своїх чисто віртуальних методів. "Середньому" класу в ієрархії дозволяється залишати деякі чисті віртуальні методи нереалізованими, як і базовий клас. Якщо «середній» клас робить реалізації чисто віртуальний метод, то його нащадки успадковують цю реалізацію, тому вони не повинні повторно реалізувати його самостійно.


3
І навіть це (реалізація чисто віртуальних функцій) лише в тому випадку, якщо вони призначені для створення інстанцій (на відміну від того, що вони самі є абстрактним базовим класом).
Крістіан Рау,

1
Це я думав. Але я роблю це у своєму проекті, і я отримую помилку зв’язування, кажучи, що для Derived :: bar () існує "невирішений зовнішній символ"; Але я ніколи не оголошував бар у Derived, так чому лінкер шукає тіло функції?
mikestaub

1
@pixelpusher Звичайно, Derived::barмає функцію тіла, тобто Abstract::bar. Отже, здається, що одиниця перекладу, де це визначено (чи навіть це десь визначено?), Не пов’язана з одиницею перекладу, де вона називається.
Крістіан Рау,

2
@Rob: They only need to implement the pure ones.Це оманливе. Похідні класи також не обов'язково повинні реалізовувати чисті віртуальні функції.
Nawaz

Я ціную допомогу, але @trenki вдарив цвяхом по голові. Хоча ти також був правильним Крістіаном Рау, оскільки це НЕ було визначено.
mikestaub

47

Тільки чисто віртуальні методи повинні бути реалізовані у похідних класах, але вам все одно потрібне визначення (а не просто оголошення) інших віртуальних методів. Якщо ви його не надасте, компонувальник може дуже поскаржитися.

Отже, просто кладу {} після необов’язкового віртуального методу дає вам порожню реалізацію за замовчуванням:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Більш залучена реалізація за замовчуванням піде в окремий вихідний файл.


7

Стандарт ISO C ++ визначає, що всі віртуальні методи класу, які не є чисто віртуальними, повинні бути визначені.

Простіше кажучи правило:
Якщо ваш похідний клас перевизначає віртуальний метод базового класу, він також повинен надати визначення. Якщо ні, то базовий клас повинен надати визначення цього методу.

Відповідно до наведеного правила у прикладі коду, virtual void bar();потрібне визначення в базовому класі.

Довідково:

Стандарт C ++ 03: 10.3 Віртуальні функції [class.virtual]

Віртуальна функція, оголошена в класі, повинна бути визначена або оголошена чистою (10.4) у цьому класі або обох; але діагностика не потрібна (3.2).

Тож або ви повинні зробити функцію чисто віртуальною, або надати для неї визначення.

У ПКУ чаво doccuments це так:

Стандарт ISO C ++ визначає, що всі віртуальні методи класу, які не є чисто віртуальними, повинні бути визначені, але не вимагають ніякої діагностики на порушення цього правила [class.virtual]/8 . Виходячи з цього припущення, GCC буде випромінювати лише неявно визначені конструктори, оператор присвоєння, деструктор та віртуальну таблицю класу в блоці перекладу, що визначає його перший такий невбудований метод.

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

Рішення полягає у забезпеченні того, щоб були визначені всі нечисті віртуальні методи. Зверніть увагу, що деструктор повинен бути визначений, навіть якщо він оголошений чисто віртуальним [class.dtor]/7.


3

Так, це нормально ... вам потрібно лише реалізувати будь-які чисто віртуальні функції, щоб створити екземпляр класу, похідного від абстрактного базового класу.


1

Так, правильно, що похідний клас повинен ЗАМІСТИТИ функцію, яка є чисто віртуальною в батьківському класі. Батьківський клас, що має чисто віртуальну функцію, називається абстрактним класом лише тому, що клас дочірній повинен надавати власне тіло чистої віртуальної функції.

Для звичайних віртуальних функцій: - Не потрібно їх перевизначати, оскільки деякі дочірні класи можуть мати цю функцію, а інші - ні.

Основною метою механізму віртуальної функції є поліморфізм часу виконання, чи основна мета чистої віртуальної функції (абстрактного класу) полягає в тому, щоб зробити обов’язковим наявність однойменної функції з власним тілом.

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