【问题标题】:How C++ object keeps information about its member functionsC++ 对象如何保存有关其成员函数的信息
【发布时间】:2011-07-19 11:49:03
【问题描述】:
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 时,它如何知道要调用什么函数以及如何找到正确的方法。 当打印对象的大小时,它会打印其成员变量(+对齐)的总和大小。所以请提供一些内部信息如何调用成员函数。

谢谢, 神魔

【问题讨论】:

    标签: c++ oop object member-functions


    【解决方案1】:

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

    【讨论】:

    • 我不会说“编程基础”——某些语言会根据它们存储的有关方法的信息进行调用。如果调用是虚拟的,则 C++ 使用 vtables 来进行后期绑定。但是,是的,在这种情况下——编译器可以将调用解析为对硬编码地址的简单调用。
    【解决方案2】:

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

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

    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() 方法被声明为 virtual 类型,则在基类(即 A 类)中,并且 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

    【讨论】:

    • 首先感谢您的详细回答,理论上这个过程很清楚,但我仍然部分地得到了我的问题的答案......你能解释一下编译器如何搜索正确的成员函数的存在吗?在成员变量的情况下,如果我没记错的话,过程是使用符号表组织的。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      -- 詹姆斯·坎泽

      【讨论】:

        猜你喜欢
        • 2023-01-18
        • 2016-05-17
        • 2018-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多