简体   繁体   English

如何确定编译器是否在虚函数上使用早期绑定或后期绑定?

[英]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.

相关问题 纯虚拟函数是早期绑定(编译时)还是后期绑定(运行时)? - Are pure virtual functions early binding (compile time) or late binding (run time)? C++ 早期绑定和后期绑定 - C++ early binding and late binding 不处理指针时,调用子类(虚拟)函数(后期绑定) - Calling subclass (virtual) function (late binding), when not dealing with pointers C ++中的早期和晚期绑定+多态性 - Early and late binding + polymorphism in c++ 如果我在明确的情况下使用指针,编译器可以内联虚函数吗? - Can a compiler inline a virtual function if I use a pointer in a clear situation? C ++中早期绑定和后期绑定有什么区别? - What is the difference between early binding and late binding in C++? 如何提前终止另一个文件中定义的功能? - How can I terminate early a function that is defined in another file? 为什么派生类虚拟函数可以调用基类虚拟函数? 编译器如何实现? - Why can a derived class virtual function call a base class virtual function? How compiler implement? 什么是C ++中的早期(静态)和后期(动态)绑定? - What is early (static) and late (dynamic) binding in C++? 我如何确定 function 是虚拟的,什么时候不是虚拟的? - How do I determine what a function will be called if it's virtual and when it is not?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM