我正在从 java 迁移到 cpp,但在理解某些 cpp 功能的工作原理时遇到了一点问题。当我们使用多态或类型安全转换时,cpp 需要知道对象的确切类型。在 java 中,每个对象都有一个指向其定义类的链接,因此可以检索此信息。但在 cpp 中情况并非如此(我认为),我是说因为 sizeof() 运算符返回的内容不超过对象字段占用的内容,因此我猜这不是存储类型信息的地方。我在这里弄错了吗?如果没有,那么 cpp 如何管理多态性和东西?
4 回答
C++ 多态性仅限于虚函数。实现虚函数所需要的只是一个类共有的函数指针表,通常称为vtable。每个对象都会添加一个指向 vtable 的指针,但 vtable 本身对同一类的所有对象都是通用的。
请注意,vtable 的使用不是 C++ 标准强制要求的,但在实践中几乎是通用的。
C++ 只能对具有虚方法的类型执行动态类型自省;与 Java 不同,C++ 方法默认是非虚拟的。
将虚方法添加到类的(通常)结果是编译器在类结构中发出一个附加的隐藏槽,其中包含指向vtable的指针;vtable 包含用于虚拟方法的方法指针的插槽和指向动态类型信息的附加指针。vtable 的使用方式如下:
- 在实例上调用虚拟方法将遵循 vtable 并通过适当的 vtable 方法槽调用;
- 用虚方法调用
typeid
类的实例会跟随vtable指针,动态类型信息确定实例的实际(动态)类型; - 使用虚方法调用
dynamic_cast
类的实例将跟随 vtable 指针指向动态类型信息,并使用它来调整实例指针;这是必要的,因为 C++ 允许多重继承,因此指向同一对象的不同类型的指针可能指向内存中的不同位置。
指向vtable的指针槽意味着如果一个类(或其基类)有虚方法,那么sizeof
将大于对象成员字段的总和。
在 C++ 中,不使用 RTTI 是一个很好的做法,您可以在不使用它的情况下编写非常大的应用程序。您应该知道对象的类型,并将它们转换为适当的类型。在 Java 中您可以使用 if (obj instanceof ClassA) {},但在 C++ 中您希望看到这样的代码,即使它可以被编写。
您在询问反射,不,C++ 没有它,而且它不是故意的。Stroustrop 看到了其他语言,其中对象不断地用“你是什么”类型的消息相互询问,作为某种破坏的标志。偶然地,通过元编程可以获得有限的反射。
有一种方法可以绕过 C++ 中缺乏反射的问题。对 void* 的动态转换提供了一个指向最派生对象的指针。所以现在你只需要理解那个指针。如果您有自己的类型管理系统,则可以这样做。这并不简单,它几乎不可避免地在某处打破了语言规则,但它经常用于检查点/重启目的。