[英]How can I determine if a compiler uses early or late binding on a virtual function?
I have the following code: 我有以下代码:
class Pet {
public:
virtual string speak() const { return ""; }
};
class Dog : public Pet {
public:
string speak() const { return "Bark!"; }
};
int main() {
Dog ralph;
Pet* p1 = &ralph;
Pet& p2 = ralph;
Pet p3;
// Late binding for both:
cout << "p1->speak() = " << p1->speak() <<endl;
cout << "p2.speak() = " << p2.speak() << endl;
// Early binding (probably):
cout << "p3.speak() = " << p3.speak() << endl;
}
I have been asked to determine whether the compiler uses early or late binding for the final function call. 我被要求确定编译器是否对最终函数调用使用早期绑定或后期绑定。 I have searched online but have found nothing to help me.
我在网上搜索过,但没有找到任何帮助我。 Can someone tell me how I would carry out this task?
有人能告诉我如何执行这项任务吗?
You can look at the disassembly, to see whether it appears to be redirecting through a vtable. 您可以查看反汇编,看它是否通过vtable重定向。
The clue is whether it calls directly to the address of the function (early binding) or calls a computed address (late binding). 线索是它是直接调用函数的地址(早期绑定)还是调用计算地址(后期绑定)。 The other possibility is that the function is inlined, which you can consider to be early binding.
另一种可能性是函数内联,您可以考虑将其作为早期绑定。
Of course the standard doesn't dictate the implementation details, there may be other possibilities, but that covers "normal" implementations. 当然,标准没有规定实现细节,可能还有其他可能性,但这涵盖了“正常”实现。
You can always use hack :D 你总是可以使用hack:D
//...
Pet p3;
memset(&p3, 0, sizeof(p3));
//...
If compiler does use vtbl pointer, guess what will gonna happen :> 如果编译器确实使用了vtbl指针,那么猜猜会发生什么:>
p3.speak() // here
Look at the generated code. 查看生成的代码。 Eg in Visual Studio you can set a breakpoint, then right-click and select "Go To Disassembly".
例如,在Visual Studio中,您可以设置断点,然后右键单击并选择“转到反汇编”。
It uses early binding. 它使用早期绑定。 You have an object of type P3.
你有一个P3类型的对象。 While it is a base class with a virtual function definition, the type is concrete and known at compile time, so it doesn't have to consider the virtual function mapping to derived classes.
虽然它是具有虚函数定义的基类,但是类型是具体的并且在编译时是已知的,因此它不必考虑将虚函数映射到派生类。
This is much the same as if you called speak() in the Pet constructor - even when making derived objects, when the base class constructor is executing the type of the object is that of the base so the function would not use the v-table, it would call the base type's version. 这与在Pet构造函数中调用speak()非常相似 - 即使在创建派生对象时,当基类构造函数执行时,对象的类型也是基类的类型,因此函数不会使用v-table ,它会调用基类型的版本。
Basically, early binding is compile time binding and late binding is run-time binding. 基本上,早期绑定是编译时绑定,后期绑定是运行时绑定。 Run time binding is only used in instances where the compiler doesn't have enough type information at compile time to resolve the call.
运行时绑定仅用于编译器在编译时没有足够的类型信息来解析调用的情况。
In fact the compiler has no obligation to use either one particularly, just to make sure that the right function is called. 事实上,编译器没有义务特别使用任何一个,只是为了确保调用正确的函数。 In this case, your object is of the concrete type
Pet
, so as long as Pet::speak
is called the compiler is "doing the right thing". 在这种情况下,你的对象是具体类型
Pet
,所以只要调用Pet::speak
,编译器就是“做正确的事”。
Now, given that the compiler can statically see the type of the object, I suspect that most compilers will optimize away the virtual call but there is no requirement that they do so. 现在,鉴于编译器可以静态地查看对象的类型,我怀疑大多数编译器会优化掉虚拟调用,但并不要求他们这样做。
If you want to know what your particular compiler is doing the only way is to consult its documentation, source code, or the generated disassembly. 如果您想知道您的特定编译器正在做什么,唯一的方法是查阅其文档,源代码或生成的反汇编。
I just thought of a way to tell at runtime, without guesswork. 我只想到一种在运行时告诉的方法,而不需要猜测。 You can simply override the vptr of your polymorphic classes with 0 and see if the method is called or if you get a segmentation fault.
您可以简单地使用0覆盖多态类的vptr,并查看是否调用了方法或是否出现了分段错误。 This is what I get for my example:
这就是我的例子:
Concrete: Base
Concrete: Derived
Pointer: Base
Pointer: Derived
DELETING VPTR!
Concrete: Base
Concrete: Derived
Segmentation fault
Where Concrete: T
means that calling the virtual member function of T
through a concrete type was successful. 其中
Concrete: T
意味着调用的虚拟成员函数T
通过一个具体类型是成功的。 Analogously, Pointer: T
says that calling the member function of T
through a Base
pointer was successful. 类似地,
Pointer: T
说,调用的成员函数T
通过Base
指针是成功的。
For reference, this is my test program: 作为参考,这是我的测试程序:
#include <iostream>
#include <string.h>
struct Base {
unsigned x;
Base() : x(0xEFBEADDEu) {
}
virtual void foo() const {
std::cout << "Base" << std::endl;
}
};
struct Derived : Base {
unsigned y;
Derived() : Base(), y(0xEFCDAB89u) {
}
void foo() const {
std::cout << "Derived" << std::endl;
}
};
template <typename T>
void dump(T* p) {
for (unsigned i = 0; i < sizeof(T); i++) {
std::cout << std::hex << (unsigned)(reinterpret_cast<unsigned char*>(p)[i]);
}
std::cout << std::endl;
}
void callfoo(Base* b) {
b->foo();
}
int main() {
Base b;
Derived d;
dump(&b);
dump(&d);
std::cout << "Concrete: ";
b.foo();
std::cout << "Concrete: ";
d.foo();
std::cout << "Pointer: ";
callfoo(&b);
std::cout << "Pointer: ";
callfoo(&d);
std::cout << "DELETING VPTR!" << std::endl;
memset(&b,0,6);
memset(&d,0,6);
std::cout << "Concrete: ";
b.foo();
std::cout << "Concrete: ";
d.foo();
std::cout << "Pointer: ";
callfoo(&b);
std::cout << "Pointer: ";
callfoo(&d);
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.