【问题标题】:C++ early binding and late bindingC++ 早期绑定和后期绑定
【发布时间】:2020-03-01 02:40:07
【问题描述】:

我读到了 C++ 中的早期和后期绑定:

int add (int x, int y)
{
  return x+y;
}

int main()
{
   int a=add(5,6);//early binding
   int (*p_add)(int,int)=add;

   int b=p_add(5,19);
}

为什么不能在编译时解析int b=p_add(5,19)?我们都知道它在编译时与add 函数相关联。那为什么我们不能像添加函数一样在编译时解决它呢?我的问题是,如果我在编译时知道add(x,y),那么我也可以在编译时预测p_add

【问题讨论】:

  • 当然,在此示例中,编译器可以查看 p_add 的初始化位置并对其进行优化,如果它愿意的话。但是将p_add 移动到另一个不可能的上下文中,比如作为另一个函数的参数,看看会发生什么。
  • @RemyLebeau 那么,在上面的例子中是否发生了后期绑定?
  • 术语“早期绑定”和“后期绑定”没有出现在 C++ 标准的任何地方。 C++ 语言中没有这样的概念。因此,“是否发生后期绑定”这个问题是无法回答的,因为“后期绑定”一词没有定义。
  • 这里没有类似“后期绑定”的机会。那种东西只对虚拟方法起作用。这都是编译器的早期绑定。如果您真正要问的是通过p_add 消除取消引用,请将其设置为const 给编译器一半的机会。

标签: c++ polymorphism early-binding


【解决方案1】:

以下是 gcc 和 Clang 为您的代码生成的内容:

main:                                   # @main
    xor     eax, eax
    ret

code on Godbolt

所以在这种情况下,我们实际上并没有早期或晚期绑定。相反,我们根本没有绑定到函数——你没有使用调用函数得到的结果(直接或通过指针),所以编译器根本没有生成任何代码来调用函数全部。

我们可以使用此订单上的代码修复它:

#include <iostream>

int add (int x, int y)
{
  return x+y;
}

int main()
{
   int a=add(5,6);//early binding
   int (*p_add)(int,int)=add;

   int b=p_add(5,19);
   std::cout << b;
}

在这种情况下,编译器仍然会在编译时检测到函数的结果不依赖于任何东西,因此它会在编译时计算该值,并将其作为常量打印出来:

mov esi, 24
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)ant:

Code on Godbolt

所以,我们仍然没有对函数进行任何真正的“绑定”。让它使用直到运行时才知道的输入:

#include <iostream>
#include <cstdlib>

int add (int x, int y)
{
  return x+y;
}

int main()
{
    int x1 = rand();
    int x2 = rand();

   int a=add(x1, x2);//early binding
   int (*p_add)(int,int)=add;

   int b=p_add(x1,x2);
   std::cout << b;
}

此源产生以下目标代码:

call rand
mov ebx, eax
call rand
mov edi, OFFSET FLAT:_ZSt4cout
lea esi, [rbx+rax]
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

编译器仍然知道指针始终指向一个特定的函数,因此即使源代码显示通过指针调用的函数,在目标代码中我们不会通过指针调用函数...事实上,我们仍然根本不调用该函数。相反,函数体的代码是内联生成的。

要通过指针获取实际的函数调用,我们可以有一个指向两个不同函数中的任何一个的指针,并且在特定情况下使用这两个函数中的哪一个直到运行时才会很明显。例如:

#include <iostream>
#include <cstdlib>

int add (int x, int y)
{
  return x+y;
}

int sub(int x, int y) { 
    return x-y;
}

int main()
{
    int x1 = rand();
    int x2 = rand();

    int z = rand() % 2;

   int (*p_add)(int,int) = z ? add : sub;

   int b=p_add(x1,x2);
   std::cout << b;
}

这(终于!)通过指针调用实际上是通过指针调用:

  call rand
  mov edx, OFFSET FLAT:sub(int, int) ; start by assuming we'll subract
  mov esi, r12d
  mov edi, ebp
  test al, 1                         ; then see if we have an odd or even number
  mov eax, OFFSET FLAT:add(int, int)
  cmove rax, rdx                     ; if necessary, point to add
  call rax                           ; and finally call the function via the pointer
  mov edi, OFFSET FLAT:_ZSt4cout
  mov esi, eax
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

Code on Godbolt

总结

如果在编译时很明显将调用什么函数,编译器可能不会生成代码来通过指针调用该函数,即使源代码显示了。

【讨论】:

    猜你喜欢
    • 2016-12-15
    • 2010-10-03
    • 2012-06-12
    • 1970-01-01
    • 2011-07-17
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多