Нужно ли ВСЕ виртуальные функции реализовывать в производных классах?

91

Это может показаться простым вопросом, но я нигде не могу найти ответа.

Предположим, у меня есть следующее:

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

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

Это нормально, что класс Derived не реализует функцию bar ()? Что делать, если не ВСЕМ моим производным классам нужна функция bar (), но некоторым она нужна. Все ли виртуальные функции абстрактного базового класса должны быть реализованы в производных классах или только те, которые являются чисто виртуальными? Благодарность

Mikestaub
источник

Ответы:

82

Производные классы не должны сами реализовывать все виртуальные функции. Им нужно реализовать только чистые . 1 Это означает, что Derivedкласс в вопросе правильный. Он наследует в barреализации от своего предка класса Abstract. (Предполагается, что Abstract::barэто где-то реализовано. Код в вопросе объявляет метод, но не определяет его. Вы можете определить его встроенным образом, как показывает ответ Тренки , или вы можете определить его отдельно.)


1 И даже тогда, только если будет создан экземпляр производного класса . Если производный класс не создается напрямую, а существует только как базовый класс для более производных классов, то именно те классы отвечают за реализацию всех своих чистых виртуальных методов. «Среднему» классу в иерархии разрешено оставлять некоторые чистые виртуальные методы нереализованными, как и базовый класс. Если «средний» класс действительно реализует чистый виртуальный метод, то его потомки унаследуют эту реализацию, поэтому им не придется заново реализовывать его самостоятельно.

Роб Кеннеди
источник
3
И даже это (реализация чистых виртуальных функций), только если они предназначены для создания экземпляров (в отличие от самих абстрактных базовых классов).
Christian Rau
1
Я так и думал. Но я делаю это в своем проекте и получаю сообщение об ошибке связывания, в котором говорится, что для Derived :: bar () существует «неразрешенный внешний символ»; Но я никогда не объявлял bar в Derived, так почему же компоновщик ищет тело функции?
mikestaub
1
@pixelpusher Конечно, Derived::barесть тело функции Abstract::bar. Таким образом, кажется, что единица перевода, в которой она определена (она вообще где-то определена?), Не связана с единицей перевода, в которой она вызывается.
Christian Rau
2
@ Роб: 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

Да, это правильно, что производный класс должен ПЕРЕПРЕРЫВАТЬ функцию, которая является Pure Virtual в родительском классе. Родительский класс, имеющий чистую виртуальную функцию, называется абстрактным классом только потому, что его дочерний класс должен предоставлять собственное тело чистой виртуальной функции.

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

Основным назначением механизма виртуальной функции является полиморфизм времени выполнения, независимо от того, является ли основная цель чистой виртуальной функции (абстрактного класса) обязательной, чтобы функция имела то же имя, что и собственное тело.

CodeCodeCode
источник