C ++中早期绑定和后期绑定有什么区别?

时间:2016-04-18 09:37:56

标签: c++ oop

由于我研究面向对象编程语言,它比 C 和相当新的概念要好得多。我对早期绑定和后期绑定感到困惑 所以我的问题是

  

C ++中早期绑定和后期绑定有什么区别?

9 个答案:

答案 0 :(得分:5)

请考虑以下代码:

void foo()
{
  std::cout << "A\n";
}
void bar()
{
  std::cout << "B\n";
}

// returns a callable object which either refers to foo or bar
// depending on user input during runtime
std::function<void()> maker()
{
  std::cout << "Please give a number:\n";
  auto x = 0.0;
  if (std::cin >> x && x > 50.0)
  { // if we get a valid value from the console and if it is > 50
    // we return a callable that refers to foo
    return{ &foo };
  }
  // otherwise we return a callable that refers to bar
  return{ &bar };
}

foobar的简单调用可以在编译时解决(函数绑定可以在编译期间早期 /发生),但使用maker()将导致 late 绑定,因为编译器实际上不知道将调用哪个函数:

auto f = maker();
f(); // whether foo or bar is called depends 
     // on the user input during maker execution

对于所有其他动态调度方法(即虚函数调用)也是如此。

如果编译器无法(在编译时)证明(在运行时信息中存在)某个特定函数要在程序流的某个点调用,则使用后期函数绑定来解决相应的操作在运行时。否则,早期的函数绑定用于在编译时解析调用(可能不一定是实际的调用,但也可以通过内联来完成)。

<小时/> 编辑:鉴于以下来源,gcc(根据godbolt)对来自-O0foo的{​​{1}}和bar执行后期绑定-O2输出显示

call    t_deriv_1::f() const

bar的程序集中,因为make_bar在编译bar时已知,并且经过检查后发现实际返回的类型始终为t_deriv_1

struct t_base
{
  virtual int f() const = 0;
  virtual ~t_base() {}
};
struct t_deriv_1 : t_base
{
  int f() const override final;
};
struct t_deriv_2 : t_base
{
  int f() const override final;
};

t_base * make_foo();
t_base * make_bar()
{
  return new t_deriv_1;
}

void foo()
{
  t_base * p = make_foo();
  p->f();
  delete p;
}

void bar()
{
  t_base * p = make_bar();
  p->f();
  delete p;
}

答案 1 :(得分:2)

在早期绑定期间,编译器可以准确地解析将调用哪个函数,因此使用函数调用的函数地址。

在后期绑定期间,编译器无法推断出将被调用的确切函数,通常是因为polymorphsim。因此,函数调用在运行时解析。

答案 2 :(得分:2)

'早期绑定'适用于方法重载。编译器可以根据参数的类型确定哪些可用的重载是适用的。

'后期绑定'适用于方法覆盖。编译器无法确定哪个覆盖适用,因为它取决于调用该方法的对象的运行时类型。因此,需要通过虚函数表机制或类似机制发送运行时发送。

答案 3 :(得分:2)

让我们考虑一个例子

int add(int a, int b){
    return a + b;
}

int main(){
    int sum = add(1, 2);
    return 0;
}

这是早期绑定,因为编译器可以直接知道add的地址并在函数调用中使用它。

在两种情况下可能发生后期绑定

  
      
  1. 指向函数的指针
  2.   
int add(int a, int b){
    return a + b;
}

int sub(int a, int b){
    return a - b;
}

int main(){
    int(*p)(int, int);
    char op; //either `+` or `-`
    cin >> op;
    p = (op == '+')?add:sub;
    int result = p(10, 5);
    return 0;
}

这里函数调用由函数指针p中介,该函数指针在运行时基于op,因此编译器在编译时无法知道地址,运行时地址是已知的(运行时) 。这是后期约束。

  
      
  1. 使用基类指针
  2. 调用派生对象的方法   
class Base
{
public:
    void x() {cout << "base ";}
    virtual void y() {cout << "base ";}
};

class Derived : public Base
{
    void x() {cout << "derived ";}
    virtual void y() {cout << "derived ";}
};

int main()
{
    Base* b = new Derived;
    b->x();
    b->y();
}

这里的输出将是

base derived

b->x()调用bass类函数,因为它不是virtual,但b->y()调用派生类函数,因为它是virtual。这里的查找在运行(运行时)时占用了vtables的中间帮助。所以它是后期绑定。

经验法则: 1.编译器可以评估的内容和编译时的替换是早期绑定 2.编译器在编译时无法评估的任何内容,只能在运行时进行评估才能进行后期绑定

答案 4 :(得分:0)

早期绑定基本上是对象编程语言的概念,其中在编译时知道什么类型的对象,并且如果在编译时不知道对象的类型,则该现象被称为后期绑定和运行时绑定。

答案 5 :(得分:0)

让我们参考重载和覆盖方法的概念来理解它...所以早期绑定也称为静态绑定(编译时多态),表示在编译时解析对重载函数的调用,即哪个版本必须调用重载函数的程序在程序执行的编译时确定。

现在,在后期绑定的情况下,也称为动态绑定(运行时多态),表示在运行时解析对overriden函数的调用,即在程序执行的运行时决定必须调用哪个版本的overriden函数。

答案 6 :(得分:0)

基本上早期绑定发生在编译时也称为静态绑定,其中后期绑定在运行时也称为动态绑定。

早期绑定对象几乎是一种强类型的对象或静态类型对象。它的优点是它易于开发和高效性。它减少了运行时的错误数。

在后期绑定方法中,在运行时检查对象,变量。 后期绑定最常用的例子是使用反射和使用动态绑定。

答案 7 :(得分:0)

编译器遇到的大多数函数调用都是直接函数调用。如,

 int sum(int a, int b) {
    return a + b;
}

int main() {
    std::cout << sum(2, 3); // This is a direct function call
    return 0;
}

可以使用称为早期绑定的过程来解析直接函数调用。早期绑定(也称为静态绑定)意味着编译器能够直接将标识符名称(例如函数或变量名称)与机器地址相关联。请记住,所有功能都有唯一的机器地址。因此,当编译器遇到函数调用时,它会用一个机器语言指令替换函数调用,该指令告诉CPU跳转到函数的地址。

晚期绑定

编译器直到运行时才知道要调用哪个函数。

在某些程序中,直到运行时才能知道将调用哪个函数。这称为后期绑定(或动态绑定)。在C ++中,获得后期绑定的一种方法是使用函数指针,或者另一种方法是在继承中使用虚函数。如,

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

int subtract(int x, int y) {
    return x - y;
}
int main() {
    int x, y;
    std::cin >> x >> y;

    int operation;
    std::cout << "choose 0 for add & 1 for subtract\n";
    std::cin >> operation;

    int (*p)(int, int);  // Function Pointer

    // Set p to point to the function the user chose
    switch (operation) {
        case 0 : p = add; 
                     break;
        case 1 : p = subtract; 
                     break;
    }

    // Call the function that p is pointing to
    std::cout << "The answer is: " << p(x, y) << std::endl;

    return 0;
}

答案 8 :(得分:0)

我建议你仔细阅读。它完美地解释了您需要知道的一切 http://www.learncpp.com/cpp-tutorial/124-early-binding-and-late-binding/

  

当执行C ++程序时,它从中开始顺序执行   main()的顶部。当遇到函数调用时,指向   执行跳转到被调用函数的开头。怎么样   CPU是否知道这样做?

     

编译程序时,编译器会转换每个语句   将您的C ++程序转换为一行或多行机器语言。每一行   机器语言给出了自己独特的顺序地址。这个   对于函数没有什么不同 - 当遇到函数时,它就是   转换成机器语言并给出下一个可用地址。   因此,每个函数都以唯一的地址结束。

     

绑定是指用于转换标识符的进程   (如变量和函数名称)到地址。有约束力   用于变量和函数,在本课中我们将进行   专注于功能绑定。