纯虚函数是那些虚函数并且具有 纯说明符 ( = 0; )

第 10.4 条第 2 款 C++03 的内容告诉我们什么是抽象类,顺便说一句,如下:

[笔记:函数声明不能​​同时提供纯指定器和定义 - 预告说明] [示例:

struct C {
virtual void f() = 0 { }; // ill-formed
};

—结束示例]

对于那些不太熟悉这个问题的人,请注意 纯虚函数可以有定义 但上述子句禁止此类定义出现内联(词汇上的类内)。(对于定义纯虚函数的用途,您可能会看到,例如, 这得到了)

现在,对于所有其他种类和类型的函数,都可以提供类内定义,并且这种限制乍一看绝对是人为且难以解释的。想想看,第二眼和后来的目光似乎都是如此:)但我相信,如果没有特定的原因,这种限制就不会存在。

我的问题是:有人吗 知道 具体有那些原因呢? 好的 也欢迎大家猜测。

笔记:

  • MSVC 确实允许 PVF 具有内联定义。所以不要感到惊讶:)
  • 这个单词 inline 在这个问题中没有提到 排队 关键词。应该是这个意思 词汇上的类内
有帮助吗?

解决方案

在SO线程中 “为什么纯虚函数初始化为0?” Jerry Coffin 提供了 Bjarne Stroustrup 的这句话 C++ 的设计与演变, ,第 §13.2.3 节,我在其中添加了一些我认为相关的部分的强调:

好奇的 =0 语法是在引入新的关键字纯或抽象的明显替代方法上选择的,因为当时我没有机会接受新的关键字。有我 建议纯,版本 2.0 会有 不附带抽象类。在更好的语法之间进行选择 和抽象类,我选择了抽象类 类。而不是冒着延误和 招致某些争吵 纯粹,我使用了传统的 C 和 C++ 使用 0 表示的约定 “不在那里。”这 =0 语法适合 我认为功能主体是功能的初始化机 并且还有 (简单,但通常足够) 虚拟函数集的视图 作为 函数指针。[……]

因此,在选择语法时,Bjarne 将函数体视为声明器的一种初始化器部分,并且 =0 作为初始化程序的另一种形式,表示“没有主体”(或者用他的话说,“不在那里”)。

按理说,在那个概念图景中,一个人不可能既表示“不在那里”又拥有一个身体。

或者,仍然在那个概念图中,有两个初始化器。

现在,这就是我的心灵感应能力、google-foo 和软推理能力。我猜想没有人有足够的兴趣™向委员会提出一项关于取消这种纯粹语法限制的提案,并跟进所需的所有工作。所以现在还是这样。

其他提示

你不应该对标准化委员会抱有如此大的信心。并非所有事情都有深刻的理由来解释。有些事情之所以如此公正,是因为一开始没有人有其他想法,后来没有人认为改变它足够重要(我认为这里就是这种情况);对于足够老的东西,它甚至可能是第一个实现的产物。有些是进化的结果——曾经有一个深刻的原因,但原因被消除了,最初的决定不再被重新考虑(这里也可能是这种情况,最初的决定是因为任何定义)纯函数被禁止)。有些是不同观点之间协商的结果,结果缺乏一致性,但这种缺乏被认为是达成共识所必需的。

不错的猜测...好吧,考虑到情况:

  • 声明内联函数并提供显式内联主体(在类外部)是合法的,因此显然不反对在类内部声明的唯一实际含义。
  • 我认为语法中没有引入潜在的歧义或冲突,因此没有排除函数定义的逻辑原因 就地.

我猜:纯虚函数体的使用是在 = 0 | { ... } 语法被制定了,但语法根本没有被修改。值得考虑的是,有很多关于语言更改/增强的提案 - 包括那些使此类事情更加逻辑和一致的提案 - 但被某人采纳并写成正式提案的数量要少得多,而且数量要少得多。委员会有时间考虑,并相信编译器供应商将准备实施的那些,又要小得多。像这样的事情需要一个冠军,也许你是第一个发现其中问题的人。要了解此过程,请查看 http://www2.research.att.com/~bs/evol-issues.html.

你说,好的猜测是受欢迎的?

我觉得 = 0宣言 来自于考虑实施。这个定义很可能意味着,你得到一个 NULL RTTI 中的条目 vtbl 类信息的位置——运行时地址的位置 成员函数 存储了一个类的内容。

但实际上,当放置一个 定义 你的函数的 *.cpp 文件,你引入一个 姓名 进入链接器的目标文件:地址位于 *.o 文件在哪里可以找到特定的函数。

那么基本链接器就不再需要了解 C++ 了。它可以链接在一起,即使您将其声明为 = 0.

我想我读到你所描述的情况是可能的,尽管我忘记了这种行为:-)...

撇开析构函数不谈,纯虚函数的实现是一件奇怪的事情,因为它们永远不会以自然的方式被调用。IE。如果您有一个指向基类的指针或引用,则底层对象将始终是某个覆盖该函数的派生对象,并且始终会被调用。

实际调用实现的唯一方法是使用派生类重载之一的 Base::func() 语法。

实际上,在某些方面,这使其成为内联的更好目标,因为在编译器想要调用它时,总是清楚正在调用哪个重载。

此外,如果禁止纯虚函数的实现,则基类中的一些其他(可能受保护的)非虚函数将有一个明显的解决方法,您可以从派生函数中以常规方式调用它们。当然,范围的限制会更小,因为您可以从任何函数调用它。

(顺便说一句,我假设 Base::f() 只能使用此语法从 Derived::f() 而不是来自 Derived::anyOtherFunc(). 。我的这个假设正确吗?)。

从某种意义上说,纯虚拟析构函数是另一回事。它被用作一种技术,只是为了防止有人在其他地方没有任何纯虚函数的情况下创建派生类的实例。

“为什么”不允许这样做的实际问题的答案实际上只是因为标准委员会这么说,但我的回答揭示了我们无论如何想要实​​现的目标。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top