Почему я должен указывать чисто виртуальные функции в объявлении производного класса в Visual C ++? - PullRequest
4 голосов
/ 19 апреля 2010

Учитывая базовый класс A и производный класс B:

class A {
public:
  virtual void f() = 0;
};

class B : public A {
public:
  void g();
};

void B::g() {
    cout << "Yay!";
}

void B::f() {
    cout << "Argh!";
}

Я получаю сообщение о том, что f () не объявлено в B при попытке определить void B :: f (). Должен ли я объявить f () явно в B? Я думаю, что если интерфейс изменится, мне не нужно будет исправлять объявления в каждом производном от него классе. Нет ли способа для B автоматически получать объявления всех виртуальных функций от A?

РЕДАКТИРОВАТЬ: Я нашел статью, в которой говорится, что наследование чисто виртуальных функций зависит от компилятора: http://www.objectmentor.com/resources/articles/abcpvf.pdf

Я использую VC ++ 2008, интересно, есть ли вариант для этого.

Ответы [ 6 ]

7 голосов
/ 19 апреля 2010

Должен ли я объявить f () явно в B?

Да , вы должны объявить в классе ' определение все виртуальные функции любых базовых классов, которые вы хотите переопределить в классе. Что касается того, почему: Это так синтаксис C ++.
Обратите внимание, что ключевое слово virtual может быть опущено для объявления переопределяющих виртуальных функций:

class base {
  virtual void f();
  virtual void g();
};

class derived : public base {
  virtual void f(); // overrides base::f()
  void g();         // overrides base::g()
};

Примечание: объявление класса таково: class my_class;, тогда как это class my_class { /* ... */ }; является определением класса . Есть ограниченное количество вещей, которые вы можете сделать с классом, который был объявлен , но не определен . В частности, вы не можете создавать его экземпляры или вызывать функции-члены.
Подробнее о различиях между декларацией и определениями см. здесь .

Хорошо, в интересах обсуждения «декларация против определения», происходящего в комментариях, приведу цитату из стандарта C ++ 03, 3.1 / 2:

Объявление является определением, если оно не [...] является объявлением имени класса [...].

3.1 / 3 затем приводит несколько примеров. Среди них:

[Пример: [...]
struct S { int a; int b; }; // defines S, S::a, and S::b
[...]
struct S; // declares S
- конец примера]

Подводя итог: стандарт C ++ считает struct S; объявлением, а struct S { /*...*/ }; определением. Я считаю, что это сильная резервная копия моей интерпретации «объявление против определения» для классов в C ++.

5 голосов
/ 19 апреля 2010

Да, в C ++ вы должны четко прояснить свое намерение переопределить поведение метода базового класса, объявив (и определив) его в производном классе. Если вы попытаетесь предоставить новую реализацию в производном классе, не объявив ее в определении класса, это будет ошибкой компилятора.

2 голосов
/ 19 апреля 2010

Объявление класса C ++ определяет содержимое класса. Если вы не объявляете f () в B, похоже, вы не переопределяете его. B::f() может быть реализовано, только если вы объявите это.

0 голосов
/ 19 апреля 2010

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

Чистые виртуальные функции позволяют вам определять интерфейсный «контракт», которого ожидают все производные классы. Клиент этого класса может ожидать, что любой экземпляр класса с этим интерфейсом реализует функции в контракте.

Еще один интересный момент ... вы можете предоставить тело для чисто виртуальной функции, но оно все еще чисто и должно быть переопределено в конкретном производном классе. Преимущество предоставления тела заключается в обеспечении базового поведения, в то же время заставляя производные классы реализовывать функцию. Переопределенные функции могут затем вызывать базовую функцию Base :: F (), как и другие виртуальные функции. Когда тело не определено, вызов Base :: F () для чисто виртуальных функций является ошибкой.

0 голосов
/ 19 апреля 2010

Неужели Б не может получить все объявления виртуальных функций от A автоматически

Если вы не помечаете функцию f в A как pure virtual с = 0, функция автоматически также присутствует в любом подклассе.

class A {
public:
  virtual void f(); // not =0!
};

class B : public A {
public:
  void g();
};

void A::f() {
    cout << "I am A::f!";
}

void B::g() {
    cout << "Yay!";
}

Сейчас:

B* b = new B();
b->f(); // calls A::f
0 голосов
/ 19 апреля 2010

В вашем текущем коде вы просто наследуете функцию f () в классе B и не переопределяете член базового класса в производном классе.

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