Должна ли виртуальная функция иметь определение? - PullRequest
7 голосов
/ 27 декабря 2011

Обязательно ли иметь определение для виртуальной функции?

Рассмотрим этот пример программы ниже:

#include <iostream>

using namespace std;

class base
{
   public:
      void virtual virtualfunc();
};

class derived : public base
{
   public:
   void virtualfunc()
   {
      cout << "vf in derived class\n";
   }
};

int main()
{
   derived d;
   return 0;
}

Это дает ошибку связи:

В функции base::base() :: неопределенная ссылка на vtable for base

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

Интересно, что я обнаружил, что, если я не создаю экземпляр объекта класса derived, ошибка ссылки больше не возникает,Почему это?Какое отношение имеет инстанцирование к вышеуказанной ошибке ссылки?

Ответы [ 5 ]

11 голосов
/ 27 декабря 2011

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

Справка:

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

Виртуальная функция, объявленная в классе, должна быть определена или объявлена ​​чистой (10.4) в этом классе, или в обеих; но диагностика не требуется (3.2).

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

Если вы используете gcc, вы можете получить некоторые странные ошибки, если вы не будете следовать этой стандартной спецификации. gcc faq также документирует это:

Стандарт ISO C ++ определяет, что все виртуальные методы класса, которые не являются чисто виртуальными, должны быть определены, но не требуют какой-либо диагностики для нарушений этого правила [class.virtual]/8. Исходя из этого предположения, GCC будет генерировать только неявно определенные конструкторы, оператор присваивания, деструктор и виртуальную таблицу класса в модуле перевода, который определяет его первый такой не встроенный метод.

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

Решение состоит в том, чтобы обеспечить определение всех не чистых виртуальных методов. Обратите внимание, что деструктор должен быть определен, даже если он объявлен чисто виртуальным [class.dtor]/7.

8 голосов
/ 27 декабря 2011

Вам необходимо предоставить реализацию виртуальной функции (с ее поведением по умолчанию), если только вы не определили функцию как «чисто виртуальную».

Итак, ваш пример может быть:

class base
{
   public:
      void virtual virtualfunc() {} //intentionally do nothing;
};

или

class base
{
   public:
      void virtual virtualfunc()=0; //pure virtual;
};
3 голосов
/ 27 декабря 2011

Вам нужно либо дать определение, либо пометить его как абстрактное / чисто жизненное.

void virtual virtualfunc() = 0;
1 голос
/ 29 апреля 2013

В ответ на ошибку о vtable: виртуальная команда в этом случае говорит c ++ создать виртуальную таблицу методов в базовом классе. Таким образом, когда вы используете полиморфизм, C ++ может заменить виртуальные методы базового класса методами из производного класса с тем же именем во время выполнения. Эта ошибка говорит пользователю, что эта замена невозможна. Чтобы исправить эту ошибку, вам нужно будет либо реализовать метод, либо установить его как чисто виртуальный, добавив «= 0» в конце определения.

В ответ на изменения: причина, по которой вы не получаете сообщение об ошибке при создании экземпляра объекта в качестве базового класса, заключается в том, что базовому классу не требуется доступ к виртуальной таблице. С другой стороны, если вы на самом деле пытаетесь использовать этот метод, вы должны получить ошибку, так как никакой реализации не существует. Другими словами, даже если вы можете создать экземпляр объекта базового класса, он не является полным классом.

0 голосов
/ 27 декабря 2011

Да, вам понадобится тело, но, возможно, то, на что вы ссылаетесь, называется чисто виртуальными функциями, для которых не требуется определение в базовом классе.

Синтаксис для их определения следующий:

void virtual virtualfunc() = 0;
...