Почему виртуальная функция с только объявлением приводит к ошибке компилятора? - программирование
Подтвердить что ты не робот

Почему виртуальная функция с только объявлением приводит к ошибке компилятора?

У меня этот класс,

class Base {
    public:
        void foo();
};

int main()
{
    Base b;
}

main будет компилироваться без ошибок, хотя foo() не имеет определения. Но b.foo(); приведет к ошибке компиляции.

Более того, даже для constructor и operator= я могу просто объявить их без их определения, он будет компилироваться до тех пор, пока я их не запускаю.

Вопрос

Опять же, я добавляю виртуальную функцию в Base,

class Base {
    public:
        void foo();
        virtual void bar();  // no defition is gonna be provided.
};

Теперь main не может скомпилироваться, вместо этого я получаю сообщение об ошибке:

undefined reference to vtable for Base

Ну, это немного смущает меня, потому что раньше main мог компилироваться до тех пор, пока foo() не вызывается, но теперь я добавляю bar() и он не вызывается вообще.

Почему он не компилируется в этом случае?

4b9b3361

Ответ 1

Это строго зависит от компилятора. Диагностика не требуется ни для одного случая.

10.3 Виртуальные функции [class.virtual]

9 Функция A virtual, объявленная в классе, должна быть определена или объявлена чистые (10.4) в этом классе или оба; но не требуется диагностика(3.2). [акцент мой]

Чтобы понять, почему это происходит, давайте посмотрим, как это работает.

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

Первый пример прост - импорт требует только foo, когда он используется. Нет никакой причины, по которой линкер должен искать символ, поэтому он не делает.

Второй, с методом virtual, немного сложнее. Большинство компиляторов (если не все) требовали действительных таблиц виртуальных функций. Это означает, что во время соединения все классы, объявившие нечистые виртуальные методы, должны были бы экспортировать эти методы. Это более строгий, чем случай не virtual, потому что реализация фактически не знает, вызвана ли функция или нет (ее можно было бы назвать полиморфной).

Ответ 2

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

Но когда вы создаете виртуальную функцию, для построения vtable (для динамической отправки) нужен адрес для функции Base::bar (он ссылается на это), и поэтому компоновщику необходимо найти его реализацию.

Ответ 3

Когда создается полиморфный объект (экземпляр класса с хотя бы одной виртуальной функцией), он должен иметь указатель виртуальной таблицы, который указывает на виртуальную таблицу. Каждый полиморфный класс будет иметь виртуальную таблицу, которая будет построена один раз. Реализация компилятора должна быть неполной. Эта таблица заполняется на основе определения виртуальных функций.

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

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

Ответ 4

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