1

我有一个抽象基类,其目的是允许创建指向基类的指针数组。(对“很多事情”有用......)

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

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

是否可以创建无成员抽象基类?如果没有,是否有另一种解决方案来阻止创建我的“抽象基础”的实例?制作构造函数protected就足够了吗?

有人向我指出,实际上,如果抽象基类的目的是允许创建指向基类的向量或指针数组,则不需要抽象基类——一个人可能根本没有基类,并且使用继承层次结构顶部的类作为基类。或者,可以复制并粘贴该顶级类并将已实现的方法交换为没有实现的纯虚函数,但这在逻辑上似乎与抽象基指针的想法不一致,并且会导致更难以维护代码。

4

3 回答 3

11

提供一个纯虚析构函数:

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) 限定 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]

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

内联函数应在使用 odr 的每个翻译单元中定义,并且在每种情况下都应具有完全相同的定义 [...]

[N4431 §7.1.2/4]

...作为inline标题中的函数。


在这种情况下,该标准甚至明确规定了定义的要求:

析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。[...]

[N4431 §12.4/9]

于 2015-08-03T15:31:24.133 回答
1

是否可以创建无成员抽象基类?

最简单的方法是使析构函数成为纯虚拟的。

class AbstractBase
{
   public:
      virtual ~AbstractBase() = 0;
};
于 2015-08-03T15:31:59.483 回答
1

如果您要以多态方式删除该类的实例,那么无论如何您都必须有一个虚拟析构函数。这不仅仅是为了防止基类的实例化,还需要避免未定义的行为。把它变成纯虚拟的。并给它一个空的实现(是的,这在 C++ 中有效)。

但是,如果您根本没有使用多态性,那么您应该避免添加虚拟析构函数,而只是使构造函数受到保护。请记住,基类不一定必须建立多态类层次结构(参见 C++ 库中的示例,如std::input_iterator_tag)。

于 2015-08-03T15:50:48.443 回答