C ++早期绑定和后期绑定

时间:2020-03-01 02:40:07

标签: c++ polymorphism early-binding

我了解了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

1 个答案:

答案 0 :(得分:2)

以下是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

摘要

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

相关问题