Use-cases of pure virtual functions with body?
我最近才知道,在C++中,纯虚函数可以随意地拥有一个体。
这些函数的实际用例是什么?
经典是一个纯粹的虚拟析构函数:
1 2 3 4 5 6 | class abstract { public: virtual ~abstract() = 0; }; abstract::~abstract() {} |
您使它变得纯粹是因为没有其他东西可以这样做,并且您希望类是抽象的,但是您仍然必须提供一个实现,因为派生类的析构函数显式地调用您的。是的,我知道,这是一个非常愚蠢的教科书例子,但正因为如此,这是一个经典。它一定是在第一版的C++程序设计语言中。
不管怎样,我记不清是否真的需要实现纯虚拟函数的能力。对我来说,这似乎是这个特性存在的唯一原因,因为它必须被明确地禁止,而stroustrup没有看到这样做的原因。
如果你觉得你需要这个功能,你的设计可能走错了轨道。
纯虚拟函数,不管有没有主体,只意味着派生类型必须提供自己的实现。
如果派生类希望调用基类实现,则基类中的纯虚拟函数体非常有用。
抽象基类(带有纯虚函数)可能为它声明的纯虚函数提供实现的一个原因是让派生类具有一个他们可以选择使用的简单的"默认"值。与可以选择性重写的普通虚拟函数相比,这种方法没有太大的优势——事实上,唯一的区别是,您强制派生类显式地使用"默认"基类实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class foo { public: virtual int interface(); }; int foo::interface() { printf("default foo::interface() called "); return 0; }; class pure_foo { public: virtual int interface() = 0; }; int pure_foo::interface() { printf("default pure_foo::interface() called "); return 42; } //------------------------------------ class foobar : public foo { // no need to override to get default behavior }; class foobar2 : public pure_foo { public: // need to be explicit about the override, even to get default behavior virtual int interface(); }; int foobar2::interface() { // foobar is lazy; it'll just use pure_foo's default return pure_foo::interface(); } |
我不确定是否有很多好处——可能在设计以抽象类开始的情况下,随着时间的推移,发现许多派生的具体类正在实现相同的行为,因此他们决定将该行为转移到纯虚拟函数的基类实现中。
我认为将公共行为放入纯虚拟函数的基类实现中也是合理的,派生类可能会被期望修改/增强/增强。
全能的Habor萨特,C++标准委员会的前任主席,给出了3个场景,您可以考虑为纯虚拟方法提供实现。
必须亲自说——我觉得它们都没有说服力,一般认为这是C++的语义疣之一。似乎C++在构建抽象抽象父表的过程中脱颖而出,而不是仅仅在孩子构建或破坏过程中简单地暴露它们,然后社区专家一致建议永远不要使用它们。
一个用例是从类的构造函数或析构函数调用纯虚函数。
虚函数与体的唯一区别是纯虚函数与体的唯一区别是二次预防实例化的存在。不能在C++中标记类抽象。
在学习ODE和C++时,这个问题真的会让人困惑。就我个人而言,经常出现在我脑海中的一件事是:如果我还需要一个纯虚拟函数来实现,那么为什么首先要使它"纯"呢?为什么不将它只保留为"虚拟"的,并且已经获得了好处并覆盖了基本实现?
令人困惑的是,许多开发人员认为没有主体/实现是定义纯虚拟函数的主要目标/好处。这不是真的!身体的缺失在大多数情况下是具有纯虚拟功能的逻辑结果。拥有纯虚拟功能的主要好处是定义一个契约!通过定义纯虚拟函数,您希望强制每个派生函数始终提供自己的函数实现。这个"契约方面"非常重要,尤其是在开发类似于公共API的东西时。将函数设为仅虚函数还不够,因为衍生工具不再被迫提供自己的实现,因此您可能会失去合同方面的内容(在公共API的情况下,这可能会受到限制)。如俗话所说:可以重写虚拟函数,必须重写纯虚拟函数。在大多数情况下,契约是抽象的概念,因此相应的纯虚拟函数没有任何实现的意义。
但有时,由于生活很奇怪,您可能希望在衍生产品之间建立一个强大的契约,并且希望它们在为契约指定自己的行为的同时,以某种方式从一些默认实现中获益。即使大多数书籍作者建议避免陷入这些情况,语言也需要提供一个安全网来防止最坏的情况发生!一个简单的虚拟函数是不够的,因为可能存在逃避契约的风险。因此,C++提供的解决方案是允许纯虚拟函数也能够提供默认实现。
上面引用的Sutter文章给出了与身体具有纯虚拟功能的有趣用例。