我有一个抽象基类,它的目的是允许创建指向基类的指针数组。(对'很多事情'都很有用…)
抽象基类不包含成员函数。因此,没有纯虚方法,因此,我认为它在技术上不是抽象的。
但是,我不希望能够创建该类的实例。
是否可以创建无成员抽象基类?如果没有,是否有另一种解决方案来防止我的"抽象基"的实例被创建?构造器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
)。