3
class A {
       public :
       void printSometext() {
       std::cout << "printing A" << std::endl;
       }
    };
class B {
       public : 
       void printSometext() {
       std::cout << "printing B" << std::endl;
       }
    };

int main() {
   A* a = new A();
   a->printSometext();
   return 0;
}

C++ 对象如何保存有关其成员函数的信息。让我们考虑上面的代码。当我在对象“a”上调用 printSometext 时,它如何知道要调用什么函数以及如何找到正确的方法。在打印对象的大小时,它会打印其成员变量(+对齐)的总和大小。所以请提供一些内部信息如何调用成员函数。

谢谢,德穆斯

4

4 回答 4

7

你把 C++ 编程的基础知识弄错了。a在运行时不知道printSomeText,编译器和链接器将上述代码转换为执行这些任务的二进制代码。在运行时,只有一堆指令。

于 2011-03-21T07:25:26.497 回答
4

好吧,这是一个有趣的问题,但让我尝试以非常有条理的方式回答这个问题!

假设编译器必须解决这样的调用:*

a->someFunc();

*。

现在,编译器将有条不紊地执行以下步骤。

1.) 首先,编译器知道变量 a 的声明类型,所以它会检查对象 a ( lets call this, class A for time being) 的声明类型是否有一个名为 someFunc() 的方法,并且它必须是公共的。此方法可以在 中声明 class A,也可以是 A 类的基类之一的派生方法,但这对编译器无关紧要,它只是使用访问说明符来检查它是否存在public

  • 不用说,此步骤中的任何错误都会导致编译器错误。

2.) 其次,一旦方法被验证为 A 类的一部分,编译器必须解析对正确方法的调用,因为许多方法可能同名(由于函数重载)。这个解决正确方法的过程称为overloading resolution。编译器通过将被调用方法的签名与作为类的一部分的所有重载方法进行匹配来实现这一点。因此,someFunc() s将找到并进一步考虑所有唯一正确的 someFunc() (将签名与调用的方法匹配)。

3.) 现在是困难的部分,很可能 someFunc() 可能已在类 A ( lets call this class AA and needless to say it is some subclass of A) 的子类之一中被覆盖,并且变量 a (声明为 A 类型)实际上可能是指到 AA 类的对象,(这在 C++ 中允许启用多态性)。现在,如果 someFunc() 方法在基类(即 A 类)中声明为 type virtual,并且 someFunc() 已被 A 的子类(在 AA 中或 A 和 AA 之间的类)覆盖,则编译器必须找出正确版本的 someFunc()。

  • 现在,假设您是编译器,并且您的任务是查找该类是否AA具有此方法。显然,AA 类将具有此方法,因为它是 A 的子类,并且 A 类中 A 的公共访问已在步骤 1 中由编译器验证!. 但或者,如前一段所述, someFunc() 可能被 AA 类(或 A 和 AA 之间的任何其他类)覆盖,这是编译器需要捕获的。因此,您(因为您正在玩编译器)可以进行系统检查以找到最底层(继承树中最低)的重写方法 someFunc() 从类 A 开始并在类 AA 结束。在此搜索中,您将寻找与在重载解决方案中验证过的相同的方法签名。该方法将是将被调用的方法。

  • 现在,您可能想知道,“这到底是怎么回事”,每次搜索都完成了吗?......嗯,不是真的。编译器知道每次找到这个的开销,因此,维护一个Virtual Table为每个类类型调用的数据结构。将虚拟表视为从方法签名(可公开访问)到函数指针的映射。该虚拟表由编译器在编译过程中生成,并在程序执行期间保存在内存中。在我们的示例中,A 类和 AA 类都有自己的虚拟表。而当编译器必须在AA类中查找someFunc()时(因为变量a指向的实际对象是AA类型),它会简单地通过AA类的虚表找到函数指针。这就像散列到表中一样简单,并且是一个恒定时间操作。

问候

AVID

于 2011-03-21T08:00:20.423 回答
3

添加到 Asha 的答案(+1):

如果您真的想了解内部工作原理,我建议您学习汇编并探索生成的机器代码。这就是我所做的。

于 2011-03-21T07:28:25.600 回答
1

编译器知道变量的类型。当您使用 . 或 -> 运算符,它在右侧参数的类类型的上下文中查找左侧的符号。所以在你的例子中,它只找到 A::printSomeText。

——詹姆斯·坎泽

于 2011-03-21T09:53:28.070 回答