[英]C++: How to create an abstract base class if class has no member functions?
我有一个抽象基类,其目的是允许创建指向基类的指针数组。 (对于“很多事情有用...”)
我的抽象基类不包含任何成员函数。 因此,没有纯粹的虚拟方法,因此我想它在技术上不是抽象的。
但是,我不希望能够创建此类的实例。
是否可以创建无成员抽象基类? 如果不是,是否还有其他解决方案可以阻止创建“抽象库”实例? 使构造函数protected
就足够了吗?
曾经向我指出,实际上,如果抽象类的目的是允许创建指向该基类的向量或指针数组,则不需要该抽象基类-一个人可能根本就没有基类,并且将继承层次结构顶部的类用作基类。 或者,可以复制并粘贴该顶级类,并在没有实现的情况下交换纯虚拟函数的已实现方法,但是,这在逻辑上似乎与抽象基本指针的思想不一致,并且会导致维护代码更加困难。
提供一个纯虚拟析构函数:
struct Base {
virtual ~Base() = 0;
};
inline Base::~Base() {}
您需要提供一个实现,可以通过将其设置为inline
来直接在标头中。
抽象类是具有某些纯虚函数的类:
[...]如果一个类至少具有一个纯虚函数,则它是抽象的。 [...]
[N4431§10.4/ 2]
由于您想要一个指向抽象类实例的指针数组(派生类),因此我假设您还希望最终能够通过这些指针delete
并从而破坏这些实例中的一个或多个:
Base * instance = // ... whatever ...
delete instance;
在这种情况下,要调用(派生类的)正确的析构函数,该析构函数必须是虚拟的。
因此,由于这两种方法都是虚拟的,并且您不希望使用某些纯虚拟成员函数,因此最好使析构函数成为纯虚拟的。
要使虚拟函数纯净,请将pure-specifier附加到其声明中:
struct Foo {
virtual void bar(void) /* the */ = 0; // pure-specifier
};
现在,关于定义,您想知道为什么我们需要提供一个定义,因为...
[...]仅当使用合格ID语法(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]
...因此必须在类定义之外进行定义。 这可以在单独的源文件中完成,或者要感谢...
内联函数应在使用过的每个翻译单元中定义,并且在每种情况下都应具有完全相同的定义。
[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
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.