由于我研究面向对象编程语言,它比 C 和相当新的概念要好得多。我对早期绑定和后期绑定感到困惑 所以我的问题是
C ++中早期绑定和后期绑定有什么区别?
答案 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 };
}
对foo
或bar
的简单调用可以在编译时解决(函数绑定可以在编译期间早期 /发生),但使用maker()
将导致 late 绑定,因为编译器实际上不知道将调用哪个函数:
auto f = maker();
f(); // whether foo or bar is called depends
// on the user input during maker execution
对于所有其他动态调度方法(即虚函数调用)也是如此。
如果编译器无法(在编译时)证明(在运行时信息中存在)某个特定函数要在程序流的某个点调用,则使用后期函数绑定来解决相应的操作在运行时。否则,早期的函数绑定用于在编译时解析调用(可能不一定是实际的调用,但也可以通过内联来完成)。
<小时/> 编辑:鉴于以下来源,gcc(根据godbolt)对来自
-O0
和foo
的{{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
的地址并在函数调用中使用它。
在两种情况下可能发生后期绑定
- 指向函数的指针
醇>
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
,因此编译器在编译时无法知道地址,运行时地址是已知的(运行时) 。这是后期约束。
- 使用基类指针
调用派生对象的方法 醇>
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
的中间帮助。所以它是后期绑定。
答案 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 ++程序转换为一行或多行机器语言。每一行 机器语言给出了自己独特的顺序地址。这个 对于函数没有什么不同 - 当遇到函数时,它就是 转换成机器语言并给出下一个可用地址。 因此,每个函数都以唯一的地址结束。
绑定是指用于转换标识符的进程 (如变量和函数名称)到地址。有约束力 用于变量和函数,在本课中我们将进行 专注于功能绑定。