Почему я должен повторно объявить виртуальную функцию при переопределении [C++]

#include <iostream>
using namespace std;

class Duck {
public:
        virtual void quack() = 0;
};

class BigDuck : public Duck {
public:
  //  void quack();   (uncommenting will make it compile)

};

void BigDuck::quack(){ cout << "BigDuckDuck::Quack\n"; }

int main() {
        BigDuck b;
        Duck *d = &b;
        d->quack();

}

Код выше не компилирует. Однако, когда я объявляю виртуальную функцию в подклассе, затем это компилирует прекрасный.

Если компилятор уже имеет подпись функции, которую подкласс переопределит, то, почему redeclaration требуется?

Какое-либо понимание?

27
задан liang 3 September 2014 в 15:42
поделиться

7 ответов

Передекларирование необходимо, потому что:

  • Стандарт говорит об этом.
  • Это облегчает работу компилятора, поскольку не нужно подниматься по иерархии, чтобы проверить, существует ли такая функция.
  • Возможно, вы захотите объявить ее ниже в иерархии.
  • Чтобы инстанцировать класс, компилятор должен знать, что этот объект конкретный.
22
ответ дан 28 November 2019 в 05:17
поделиться

Если вы измените:

virtual void quack() = 0;

на

virtual void quack();

, он будет компилироваться без реализации quack () в HugeDuck.

= 0; в конце объявления функции, по сути, говорится, что все BigDucks будут крякать, но это должно быть реализовано каждой производной уткой. Удалив = 0; будет вызываться шарлатан BigDuck, если вы не реализуете шарлатан в HugeDuck.

РЕДАКТИРОВАТЬ: Чтобы уточнить = 0; говорит, что производный класс будет иметь определение функции. В вашем примере ожидается, что HugeDuck определит quack (), но, поскольку вы это закомментировали, это не так.

В качестве примечания, поскольку все утки могут крякать, возможно, ваш исходный класс Duck, который мы не видим, должен вместо этого реализовать quack ()?

11
ответ дан 28 November 2019 в 05:17
поделиться

Поскольку C ++ отделяет «объявление» от «полиморфизма»: любая функция нуждается в объявлении для компилятора, независимо от виртуальный он или нет.

Ваш пример не идет достаточно далеко, у него есть проблема «абстрактного класса»: BigDuck не может быть создан, потому что в его интерфейсе нет реализации шарлатана.

Обобщая проблему, мы можем объявить базовую функцию , а не чистой виртуальной:

class Duck { public: virtual void quack(){} };

class BigDuck : public Duck {}; 
void BigDuck::quack(){ cout << "QUACK!"; }//overrides, but doesn't declare

Здесь компилятор будет жаловаться, что у него есть символ BigDuck :: quack , который не был Не заявлено. Это не имеет ничего общего с абстрактными классами или чем-то еще.

(Примечание: gcc говорит: ошибка: в классе BigDuck не объявлена ​​функция-член void BigDuck :: q () )

6
ответ дан 28 November 2019 в 05:17
поделиться

Определение Quack() в вашем базовом классе является "абстрактным" - оно не имеет реализации. Это говорит компилятору, что ваш производный класс должен реализовать его. Неспособность сделать это является ошибкой компиляции.

2
ответ дан 28 November 2019 в 05:17
поделиться

BigDuck может быть другим абстрактным классом, и вы, возможно, не захотите реализовать шарлатанство, пока не дойдете до базового класса ReallyBigDuck.

2
ответ дан 28 November 2019 в 05:17
поделиться

Пока вы не предоставите реализацию, все классы, наследующие от класса, содержащего Чистую виртуальную функцию , являются абстрактными - их нельзя создать . Чтобы предоставить такую ​​реализацию, вы должны объявить функцию в классе.

1
ответ дан 28 November 2019 в 05:17
поделиться

Объявление методов в каждом классе сообщит компилятору, что класс предоставляет различные реализации для метода.

Кроме того, если вы хотите создать объекты BigDuck в стеке, то как компилятор должен знать сигнатуру quack () .

BigDuck aDuck;
aDuck.quack();
1
ответ дан 28 November 2019 в 05:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: