3

由于明显的原因,下面的程序无法编译:

#include <iostream>
using namespace std;

class A {
 public:
  A() { pVirt(); }
  virtual void pVirt() const = 0 { count<<"A::pVirt()"; }
};

int main() {
 A aObj;
 aObj.pVirt();
 reutrn 0;
}

问题: 1. 签名“virtual void pVirt() const = 0”中的 0 是什么意思?这是表示 vtable 中的 NULL 内存偏移量还是只是语法约束?

  1. 如果 0 是 NULL 内存偏移量(如果是的话)那么为什么 VC++ 不允许指定另一个内存地址,这就是我们不能从外部构造函数调用纯虚函数的原因(可能是因为 vtable 是在对象完全构造后创建。)?
4

4 回答 4

5

签名“virtual void pVirt() const = 0”中的 0 表示什么?

该部分=0称为纯说明符。它使虚函数,类抽象

纯虚函数不需要定义。您可以选择在类外部提供定义,并且非抽象派生类仍然必须覆盖该函数。

class A
{
 public:
     virtual ~A() {};
     virtual void f() =0;
};

void A::f() { std::cout << "A::f" << std::endl; } //optional

的定义f不会使类成为非抽象类,因此您不能创建 的实例A

A a; //error - A is abstract

此外,派生类必须重写A::f才能成为非抽象类:

class B : public A {};

B b; //error : B is still an abstract class as it didn't override A::f

class C : public A { void f() {} };

C c; //okay : C override A::f

而派生类实现可以选择调用基类实现:

class D : public A { void f() {  A::f(); } }; //defaults to A::f

D d; //okay : D override A::f, but calls A::f internally

希望有帮助。

于 2013-02-06T12:23:32.117 回答
4

这只是表示该函数是纯虚函数的语法。它没有任何实际意义。C++ 设计者也可以选择使用pureabstract代替= 0. 我怀疑不这样做的唯一原因是他们不想在语言中引入新的保留字(因为这会破坏任何已经使用新保留字作为标识符的现有代码)。

(没有必要保留这样一个词,因为它是一个上下文相关的关键字,但是上下文相关的关键字的概念直到最近才进入主流用法,而且这种语法更古老。)

于 2013-02-06T12:22:05.470 回答
2

函数声明中的= 0只是语法:两个标记序列意味着函数是纯的,仅此而已。而且你不能用其他任何东西替换任何一个标记:= 0L例如,这是不合法的。

您收到错误的原因仅仅是因为不允许使用语法。由于历史原因,如果没有别的。如果要为纯虚函数提供定义,则必须在类之外进行。

如果提供一个纯虚函数的定义,你可以从构造函数中调用它;您只需要停用虚拟呼叫机制;例如A::pVirt()。如果实际的函数调用涉及动态解析,并且解析结果是纯虚函数,则无论函数是否定义,都是未定义行为。这里的动机是让你不要定义它;通常,无论你调用与否,都必须定义一个虚函数,因为编译器必须将它的地址放在 中vtable,如果没有定义,就没有地址。使函数成为纯虚函数告诉编译器它不应该将它的地址放在vtable. 所以你不必定义它。但是如果您尝试通过 vtable 调用该函数,您可能会感到惊讶。

于 2013-02-06T12:33:24.610 回答
1

虚函数后面的= 0意思是“这是纯虚函数,必须在派生函数中实现”。与您的示例一样,仍然可以实现该功能。它本身没有其他含义——您不能使用其他数字、地址或其他任何东西。你可以有几个函数=0,它仍然是相同的含义。

于 2013-02-06T12:23:52.297 回答