Должен ли чистый виртуальный класс С++ определить определение?
Рассмотрим:
// in header.h
class A {
public:
virtual ~A() = 0;
};
class B : public A {
public:
~B() override {}
}
Компонент сообщает, что он не может решить:
внешний символ "public: virtual __thiscall A:: ~ A (void)" ссылка в функции "public: virtual __thiscall B:: ~ B (void)"
Я нахожу, что мне нужно написать определение A::~A()
.
Раньше я думал, что чистый виртуальный класс определяет интерфейс (объявление функции), и они не должны содержать определение функции. Должен ли я писать определения для всех виртуальных функций в чистом виртуальном базовом классе? Или мне просто нужно написать функцию деструктора?
Ответы
Ответ 1
Это потому, что в отличие от обычных виртуальных функций деструктор не просто переопределяется. Когда вы вызываете обычную виртуальную функцию в базовом классе, вы вызываете только вызов функции, которая переопределила эту функцию.
В случае деструктора, однако, также должны быть вызваны деструкторы базовых классов. Но так как вы не предоставляете реализацию ~A()
, ваш код не может ссылаться.
Но вы можете определить функцию, даже если она чистая виртуальная: как здесь.
Ответ 2
Нужно ли определению С++ чистого виртуального класса?
Первая ошибка заключается в том, что вы создаете условия от своего имени. Нет такой вещи, как "чистый виртуальный класс". Существует только "виртуальная функция" и "чистая виртуальная функция". Понимая, что нет такой вещи, "чистый виртуальный класс" является ключом к пониманию того, почему этот вопрос не выполняется.
Раньше я думал, что чистый виртуальный класс определяет интерфейс (объявление функции)
Я думаю, что С++ - это не ваш первый язык, а второй язык после Java/С#, и вы думаете в Java/С# идеях, которые не имеют ничего общего с С++.
С++ имеет интерфейсы - это просто объявление класса:
struct A{
void doNothing();
};
//A.cpp:
void A::doNothing(){
}
Это интерфейс структуры A. Имеет ли это какое-либо отношение к наследованию, виртуальным функциям или полиморфизму? Нет. Это просто объявление класса, какой метод и свойства существуют внутри него.
Каждому классу нужен действительный деструктор, позволяющий программе очищать ресурсы объекта - память и т.д. Это не имеет ничего общего с полиморфизмом. В вашем примере A
нужно каким-то образом разрушить. Не имеет значения, что B
наследует от него. Он должен сообщить программе, как справиться с ней, когда она выходит из сферы действия.
Как уже упоминалось в комментариях, если вам нужен только виртуальный деструктор (так что UB не будет проявляться в случае A* a = new B()
), просто объявите деструктор по умолчанию:
virtual ~A() = default;
Ответ 3
В дополнение к моим предшественникам вы можете прочитать об этом здесь.
Может быть предоставлено определение чистой виртуальной функции (и должно быть предоставлено, если чистый виртуальный объект является деструктором): функции-члены производного класса могут называть абстрактную базовую чистую виртуальную используя квалифицированный идентификатор функции. Это определение должно быть предоставлено вне тела класса (синтаксис объявления функции не допускает как чистый спецификатор = 0, так и тело функции)
Ответ 4
Чтобы сделать реферат класса, вам нужно сделать хотя бы одну виртуальную функцию чистой. Если у вас нет другого, тогда деструктор может быть хорошим кандидатом. Но с другой стороны деструктор - это особый случай - вы должны определить его, даже если он чист.
Примечание: вы также можете определить обычную чистую виртуальную функцию, например, чтобы перехватить условие ошибки (что-то вроде чистой функции вызывается из ctor или dtor), но, в отличие от деструктора, отлично не предоставлять такое определение, и компилятор будет генерировать обработчик ошибок там, который сообщит, что называется чистая виртуальная функция.