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时,它如何知道要调用的函数以及它如何找到正确的方法。 打印对象的大小时,它会打印其成员变量的总和大小(+ allignments)。因此,请提供成员函数调用的内部信息。
谢谢, Deimus
答案 0 :(得分:7)
你已经弄错了C ++编程的基础知识。 a
在运行时不知道printSomeText
,它是编译器和链接器,它将上面的代码转换为执行这些任务的二进制代码。在运行时,只有一堆指令。
答案 1 :(得分:4)
嗯,这是一个有趣的问题,但让我试着以非常有条不紊的方式回答这个问题!
假设编译器必须解决这样的调用:*
A-&GT; 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
答案 2 :(得分:3)
添加Asha的答案(+1):
如果你真的想了解内部的工作原理,我建议学习汇编并探索生成的机器代码。这就是我所做的。
答案 3 :(得分:1)
编译器知道变量的类型。当你使用 。或 - &gt;运算符,它在符号中向左查找符号 右手参数的类类型的上下文。所以 例如,它只找到A :: printSomeText。
- 詹姆斯坎泽