我试图理解在 C++ 中调用函数的两种方式之间的区别,特别是在处理虚函数时。这是一个简单的代码示例:
#include <iostream>
class A {
public:
virtual void main() {
std::cout << "virtual void main of A" << std::endl;
}
int fun() {
std::cout << "fun function: " << (long long)((void*)&A::main) << std::endl;
// Member function pointer pointing to A::main
auto ptr = &A::main;
// Using 'this' pointer to call the function pointer
(this->*ptr)();
// Explicitly calling A::main without virtual mechanism
this->A::main();
return 0;
}
};
class B : public A {
public:
void main() override {
std::cout << "virtual void main of B" << std::endl;
}
};
int main() {
B* b = new B;
A* a = b;
b->fun();
a->fun();
delete b;
return 0;
}
输出:
fun function: 4205984
virtual void main of B
virtual void main of A
fun function: 4205984
virtual void main of B
virtual void main of A
我很困惑为什么 (this->*ptr)();和 this->A::main();产生不同的结果。两者似乎都在访问 A::main,但输出表明它们的行为不同。有人可以解释一下为什么 (this->*ptr)();调用 B::main() 而 this->A::main();调用 A::main()?虚函数机制如何影响这些调用?就虚拟表查找或直接函数调用而言,内部会发生什么?
通过成员函数指针进行的调用将调用它所指向的虚函数的最终重写者,就像使用引用同一成员函数的非限定名称的普通直接成员函数调用一样。
使用成员函数限定名称的成员函数调用不会调用最终的重写器。相反,它会调用通过名称查找找到的函数,就好像它不是
virtual
。
这就是语言的指定方式。
旁注:
(long long)((void*)&A::main)
格式错误。如果您的编译器在没有诊断的情况下允许它,那么它的行为不符合标准。无论它产生什么输出,都与程序行为无关。
auto ptr = &A::main;
为您提供了一个指向 main
的成员函数指针(其类型为
void (A::*)()
)。它不是原始函数指针,它存储的内容更像是 main
的 vtable 条目的偏移量。当您在 B
上调用它时,它会在那里找到被覆盖的 main
并调用它,就像您完成了 whatever->main()
一样。
另一方面,this->A::main()
的含义如下:在 A::main
上调用 this
,而不考虑它是否已被覆盖。