C++多态场景下编译器如何识别指针的实际类型?
问题解答
关于编译器是否能获知指针实际类型的问题
首先明确:编译器在编译阶段完全不知道Parent* p的实际指向类型,你所说的动态调用(调用子类重写的foo)也完全不需要编译器修改指针p的类型,这一能力由C的虚函数机制实现,依赖对象自身携带的运行时类型信息,和你传入的只有对象地址的前提完全不冲突。
主流C编译器的通用实现逻辑如下:
- 你提到的"重写"本身隐含了
Parent::foo是虚函数的前提(C++中只有父类声明为virtual的成员函数才能被子类重写),只要Parent类定义了虚函数,所有Parent类及其派生类的实例内存头部都会隐含一个虚表指针,指向当前类对应的虚函数表,虚函数表中按顺序存储了当前类所有虚函数的实际入口地址。 - 运行
p->foo()时,程序会直接从p指向的内存首地址取出虚表指针,再查询虚表中foo对应的函数地址完成调用。整个过程只需要访问对象自身的内存内容,不需要额外信息,也不需要修改p本身的类型。
如果Parent::foo没有声明为virtual,就不存在子类重写的语义,只是子类定义了同名函数隐藏了父类实现,此时p->foo()的调用地址在编译期就会被固定为Parent::foo,不管p实际指向什么子类对象,都不会调用子类的实现。
关于两段代码的运行表现对比
你给出的两段代码唯一的区别是Child实例的存储位置:
// 第一段:Child实例在栈上 Child c; Parent *p = &c; p->foo(); // 第二段:Child实例在堆上 Parent *p = new Child(); p->foo();
两段代码的运行表现完全一致:
- 如果
foo是虚函数,都会调用Child::foo - 如果
foo不是虚函数,都会调用Parent::foo
不需要编译器做任何修改指针类型的操作,二者的多态表现没有任何区别。
内容的提问来源于stack exchange,提问作者Yibo
相关产品推荐
相关产品推荐

