У меня есть абстрактный базовый класс, который служит для создания массива указателей на базовый класс. (Полезно для «многих вещей» …)
Мой абстрактный базовый класс не содержит функций-членов. Поэтому нет чисто виртуальных методов, и поэтому я думаю, что это не является технически абстрактным.
Однако я не хочу иметь возможность создавать экземпляр этого класса.
Можно ли создать абстрактный базовый класс без членов? Если нет, есть ли другое решение для предотвращения создания экземпляра моей «абстрактной базы»? Будет ли конструктор protected
хватает?
Мне было указано, что на самом деле абстрактный базовый класс не потребуется, если целью этого класса является создание вектора или массива указателей на базовый класс — можно просто не иметь базового класса и используйте в качестве базового класса класс в верхней части иерархии наследования. В качестве альтернативы можно скопировать и вставить этот топ-класс и заменить реализованные методы на чисто виртуальные функции без реализации, однако это кажется логически несовместимым с идеей абстрактных базовых указателей и может привести к усложнению поддержки кода.
Предоставьте чистый виртуальный деструктор:
struct Base {
virtual ~Base() = 0;
};
inline Base::~Base() {}
Вам нужно предоставить реализацию, которую вы могли бы исправить в заголовке, сделав ее inline
,
Абстрактный класс — это класс с какой-то чисто виртуальной функцией:
[…] Класс является абстрактным, если он имеет хотя бы одну чисто виртуальную функцию. […] [N4431 §10.4 / 2]
Поскольку вам нужен массив указателей на экземпляры (классов, производных от) вашего абстрактного класса, я предполагаю, что вы также захотите delete
и поэтому разрушать один или несколько таких экземпляров с помощью этих указателей:
Base * instance = // ... whatever ...
delete instance;
Чтобы вызвать правильный деструктор (производного класса) в этом случае, деструктор имеет быть виртуальным.
Так как в любом случае это виртуально, и вам не нужна какая-то чисто виртуальная функция-член, лучше всего сделать деструктор чисто виртуальным.
Чтобы сделать виртуальную функцию чистой, вы добавляете чистый спецификатор к его объявлению:
struct Foo {
virtual void bar(void) /* the */ = 0; // pure-specifier
};
Теперь, что касается определения, вам интересно, почему мы должны предоставить его, так как …
[…] Чистая виртуальная функция должна быть определена только в том случае, если она вызывается с помощью или, как если бы с (12.4), синтаксис квалифицированного идентификатора (5.1). […] [N4431 §10.4 / 2]
Это связано с тем, что при уничтожении производного класса после вызова деструктора производных классов также будут вызываться деструкторы базовых классов:
struct Derived : public Base {
~Derived() {
// contents
// Base::~Base() will be called
}
};
После выполнения тела деструктора […] деструктор для класса X вызывает […] деструкторы для прямых базовых классов X и, если X является типом самого производного класса (12.6.2), его деструктор вызывает деструкторы для виртуальных базовых классов X. Все деструкторы называются так, как если бы на них ссылались с определенным именем […] [N4431 §12.4 / 8]
Таким образом, определение чистого виртуального деструктора, если Base
класс нужен. Тем не мение …
[…] Объявление функции не может предоставить ни чистый спецификатор, ни определение […] [N4431 §10.4 / 2]
… поэтому он должен быть определен вне определения класса. Это можно сделать в отдельном исходном файле или благодаря …
Встроенная функция должна быть определена в каждой единице перевода, в которой она используется odr, и должна иметь точно такое же определение в каждом случае […] [N4431 §7.1.2 / 4]
… как inline
функция в шапке.
Стандарт даже явно говорит о требовании определения в этом случае:
Деструктор может быть объявлен виртуальным (10.3) или чисто виртуальным (10.4); если какие-либо объекты этого класса или любого производного класса созданы в программе, деструктор должен быть определен. […] [N4431 §12.4 / 9]
Можно ли создать абстрактный базовый класс без членов?
Самый простой способ — сделать деструктор чисто виртуальным.
class AbstractBase
{
public:
virtual ~AbstractBase() = 0;
};
Если вы собираетесь полиморфно удалять экземпляры этого класса, то вы должен В любом случае иметь виртуальный деструктор. Это не просто для предотвращения создания экземпляров базового класса, это требуется чтобы избежать неопределенного поведения. Просто сделайте это чисто виртуальным. И дать ему пустую реализацию (да, это работает в C ++).
Однако, если вы вообще не используете полиморфизм, вам следует избегать добавления виртуального деструктора и вместо этого просто сделать конструктор защищенным. Помните, что базовый класс не обязательно должен устанавливать полиморфную иерархию классов (см. Примеры в библиотеке C ++, например: std::input_iterator_tag
).