C ++: как создать абстрактный базовый класс, если у класса нет функций-членов?

У меня есть абстрактный базовый класс, который служит для создания массива указателей на базовый класс. (Полезно для "многих вещей" ...)

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

Однако я не хочу создавать экземпляр этого класса.

Можно ли создать абстрактный базовый класс без членов? Если нет, есть ли другое решение для предотвращения создания экземпляра моей «абстрактной базы»? Достаточно ли сделать конструктор protected?

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


person FreelanceConsultant    schedule 03.08.2015    source источник
comment
он содержит переменные-члены?   -  person David Haim    schedule 03.08.2015
comment
Какой в ​​этом толк? Это базовый класс, но в нем ничего нет?   -  person Barry    schedule 03.08.2015
comment
@Barry Ты не читал вопрос? (Useful for 'many things'...);)   -  person fredoverflow    schedule 03.08.2015
comment
@DavidHaim Это не так   -  person FreelanceConsultant    schedule 03.08.2015
comment
Поскольку он не содержит виртуальных методов, я полагаю, вам нужно, чтобы он содержал хотя бы одну переменную, которая позже сообщит вашему коду, каков ее тип на самом деле, чтобы вы могли переделать ее перед использованием (хотя это не часть вашего вопроса).   -  person Havenard    schedule 03.08.2015
comment
Это дубликат наследования от пустого базового класса в C ++. За исключением того, что лучший ответ там содержит (исправляемую) ошибку. Если деструктор определен, это нужно делать вне определения класса.   -  person ex-bart    schedule 03.08.2015
comment
@ ex-bart: Это неправда. Он может быть встроен, как и любая другая функция-член.   -  person Christian Hackl    schedule 03.08.2015
comment
@ChristianHackl C ++ 11 §10.4 [class.abstract] / 2: [Примечание: Объявление функции не может содержать одновременно спецификатор и определение - конец примечания]   -  person ex-bart    schedule 03.08.2015
comment
@ChristianHackl Но я полагаю, что есть компиляторы, которые позволяют это. Но не g ++ - 4.8.   -  person ex-bart    schedule 03.08.2015
comment
@ ex-bart: Вы правы, меня обманула VC, которая, по крайней мере, в своей версии 2013 года принимает это.   -  person Christian Hackl    schedule 03.08.2015


Ответы (3)


Предоставьте чистый виртуальный деструктор:

struct Base {
 virtual ~Base() = 0;
};

inline Base::~Base() {}

Вам нужно предоставить реализацию, которую вы можете прямо в заголовке сделать inline.


Абстрактный класс - это класс с некоторой чистой виртуальной функцией:

[...] Класс является абстрактным, если он имеет хотя бы одну чистую виртуальную функцию. [...]

[N4431 §10.4/2]

Поскольку вам нужен массив указателей на экземпляры (классы, производные от) вашего абстрактного класса, я предполагаю, что вы также хотите иметь возможность в конечном итоге delete и, таким образом, уничтожить один или несколько из этих экземпляров с помощью этих указателей:

Base * instance = // ... whatever ...
delete instance;

Чтобы вызвать правильный деструктор (производного класса) в этом случае, деструктор должен быть виртуальным.

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

Чтобы сделать виртуальную функцию чистой, вы добавляете к ее объявлению pure-specier:

struct Foo {
 virtual void bar(void) /* the */ = 0; // pure-specifier
};

Теперь, что касается определения, вы задаетесь вопросом, зачем нам его давать, поскольку ...

[...] Чистая виртуальная функция должна быть определена только в том случае, если она вызывается с синтаксисом квалифицированного идентификатора (5.1) или как если бы с (12.4). [...]

[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]

person Daniel Jour    schedule 03.08.2015
comment
Как это чисто виртуально, если есть реализация? - person FreelanceConsultant; 03.08.2015
comment
потому что в объявлении есть = 0. - person ex-bart; 03.08.2015
comment
@ user3728501 это чисто виртуальный, потому что производные классы вынуждены переопределять его. - person Quentin; 03.08.2015
comment
@Quentin Я не знал, что можно объявить функцию как чисто виртуальную и одновременно предоставить реализацию. - person FreelanceConsultant; 03.08.2015
comment
@ user3728501, см. stackoverflow.com/questions/12918637/. Это объясняет, почему имеет смысл предоставить реализацию чистой виртуальной функции. - person R Sahu; 03.08.2015
comment
Спасибо всем за объяснение. Я был на мобильном телефоне, но теперь могу дать полное объяснение. - person Daniel Jour; 03.08.2015

Можно ли создать абстрактный базовый класс без членов?

Самый простой способ - сделать деструктор чисто виртуальным.

class AbstractBase
{
   public:
      virtual ~AbstractBase() = 0;
};
person R Sahu    schedule 03.08.2015

Если вы собираетесь удалить экземпляры этого класса полиморфно, тогда вам в любом случае необходимо иметь виртуальный деструктор. Это не только для предотвращения создания экземпляров базового класса, это требуется, чтобы избежать неопределенного поведения. Просто сделайте это чисто виртуальным. И дайте ему пустую реализацию (да, это работает на C ++).

Однако, если вы вообще не используете полиморфизм, вам следует избегать добавления виртуального деструктора и вместо этого просто защитить конструктор. Помните, что базовый класс не обязательно должен устанавливать иерархию полиморфных классов (см. Примеры в библиотеке C ++, например std::input_iterator_tag).

person Christian Hackl    schedule 03.08.2015