如果类没有成员函数,如何创建抽象基类

C++: How to create an abstract base class if class has no member functions?

本文关键字:创建 基类 抽象 何创建 成员 函数 如果      更新时间:2023-10-16

我有一个抽象基类,它的目的是允许创建指向基类的指针数组。(对'很多事情'都很有用…)

抽象基类不包含成员函数。因此,没有纯虚方法,因此,我认为它在技术上不是抽象的。

但是,我不希望能够创建该类的实例。

是否可以创建无成员抽象基类?如果没有,是否有另一种解决方案来防止我的"抽象基"的实例被创建?构造器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
};

现在,关于定义,你想知道为什么我们需要提供一个,因为…

[…纯虚函数只有在使用限定id语法(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)

…所以它必须在类定义之外定义。这可以在一个单独的源文件中完成,或者感谢…

一个内联函数应该在每一个使用它的翻译单元中定义,并且在任何情况下都应该有完全相同的定义[…]

[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)。