Skip to content

Latest commit

 

History

History
198 lines (140 loc) · 9.55 KB

18.5 — Early binding and late binding.md

File metadata and controls

198 lines (140 loc) · 9.55 KB

18.5 — Early binding and late binding

In this lesson and the next, we are going to take a closer look at how virtual functions are implemented. While this information is not strictly necessary to effectively use virtual functions, it is interesting. Nevertheless, you can consider both sections optional reading.

在这一节和下一节,我们会仔细的看一下虚函数是怎么实现的。虽然这个信息对于使用虚函数没有什么帮助,但是他很有趣。不管怎么样,这两节都是选读内容。

When a C++ program is executed, it executes sequentially, beginning at the top of main(). When a function call is encountered, the point of execution jumps to the beginning of the function being called. How does the CPU know to do this?

在一个C++程序被执行的时候,它会按序的的执行,从main函数的上面开始执行。当遇到一个函数调用的时候,执行点调到了被执行的函数的开头。CPU是怎么知道要这样做的呢?

When a program is compiled, the compiler converts each statement in your C++ program into one or more lines of machine language. Each line of machine language is given its own unique sequential address. This is no different for functions -- when a function is encountered, it is converted into machine language and given the next available address. Thus, each function ends up with a unique address.

当一个函数被编译的时候,编译器把你所有的C++程序中的语句转化成多行机器语言。每一行机器指令都有它自己特定的地址。这对函数没什么区别,当遇到一个函数的时候,函数也被转换成机器语言,也给了一个可用的地址。所以每一个函数都以一个唯一的地址为结尾。

Binding refers to the process that is used to convert identifiers (such as variable and function names) into addresses. Although binding is used for both variables and functions, in this lesson we’re going to focus on function binding.

绑定指的是把变量和函数名转换为地址的过程。虽然绑定对变量和函数都是有作用的,但是在本节,我们主要关注点在函数绑定上。

Early binding

Most of the function calls the compiler encounters will be direct function calls. A direct function call is a statement that directly calls a function. For example:

大部分编译器遇到的函数调用时是一个直接的函数调用,一个直接的函数调用是一个直接调用那个函数的语句,举个例子:

#include <iostream>
 
void printValue(int value)
{
    std::cout << value;
}
 
int main()
{
    printValue(5); // This is a direct function call
    return 0;
}

Direct function calls can be resolved using a process known as early binding. Early binding (also called static binding) means the compiler (or linker) is able to directly associate the identifier name (such as a function or variable name) with a machine address. Remember that all functions have a unique address. So when the compiler (or linker) encounters a function call, it replaces the function call with a machine language instruction that tells the CPU to jump to the address of the function.

直接函数调用可以被使用早绑定的过程解析。早绑定意味着这个编译器是有能力直接把这个函数名和一个机器地址联系起来的。记住所有的函数都有一个独一无二的地址。所以当编译器遇到一个函数调用的时候,它会用机器指令替换掉这个函数调用,告诉CPU去跳到这个函数对应的地址去执行。

Let’s take a look at a simple calculator program that uses early binding:

我们看一下这个简单使用了早绑定的计算器程序:

#include <iostream>
 
int add(int x, int y)
{
    return x + y;
}
 
int subtract(int x, int y)
{
    return x - y;
}
 
int multiply(int x, int y)
{
    return x * y;
}
 
int main()
{
    int x;
    std::cout << "Enter a number: ";
    std::cin >> x;
 
    int y;
    std::cout << "Enter another number: ";
    std::cin >> y;
 
    int op;
    do
    {
        std::cout << "Enter an operation (0=add, 1=subtract, 2=multiply): ";
        std::cin >> op;
    } while (op < 0 || op > 2);
 
    int result = 0;
    switch (op)
    {
        // call the target function directly using early binding
        case 0: result = add(x, y); break;
        case 1: result = subtract(x, y); break;
        case 2: result = multiply(x, y); break;
    }
 
    std::cout << "The answer is: " << result << std::endl;
 
    return 0;
}

Because add(), subtract(), and multiply() are all direct function calls, the compiler will use early binding to resolve the add(), subtract(), and multiply() function calls. The compiler will replace the add() function call with an instruction that tells the CPU to jump to the address of the add() function. The same holds true for subtract() and multiply().

因为加减乘除是所有的的函数调用,所以程序员会使用早绑定把他们转化成机器语言的地址,告诉CPU跳转到对应的地址去执行

Late Binding

In some programs, it is not possible to know which function will be called until runtime (when the program is run). This is known as late binding (or dynamic binding). In C++, one way to get late binding is to use function pointers. To review function pointers briefly, a function pointer is a type of pointer that points to a function instead of a variable. The function that a function pointer points to can be called by using the function call operator (()) on the pointer.

在某些程序中,直到运行期才能知道函数执行地址,这个叫做迟绑定。使用迟绑定的一个办法是使用函数指针。简单的回忆一下函数指针。函数指针就是指向函数的指针。被函数指针指向的函数可以通过函数调用运算符作用在函数指针上来执行

For example, the following code calls the add() function:

举个的例子,下面的代码会调用add函数

#include <iostream>
 
int add(int x, int y)
{
    return x + y;
}
 
int main()
{
    // Create a function pointer and make it point to the add function
    int (*pFcn)(int, int) = add;
    std::cout << pFcn(5, 3) << std::endl; // add 5 + 3
 
    return 0;
}

Calling a function via a function pointer is also known as an indirect function call. The following calculator program is functionally identical to the calculator example above, except it uses a function pointer instead of a direct function call:

通过函数指针调用一个函数也被认为是间接调用。下面的计算器程序在功能上和上面的例子是一样的,除了它没有使用了一个直接的函数调用。

#include <iostream>
 
int add(int x, int y)
{
    return x + y;
}
 
int subtract(int x, int y)
{
    return x - y;
}
 
int multiply(int x, int y)
{
    return x * y;
}
 
int main()
{
    int x;
    std::cout << "Enter a number: ";
    std::cin >> x;
 
    int y;
    std::cout << "Enter another number: ";
    std::cin >> y;
 
    int op;
    do
    {
        std::cout << "Enter an operation (0=add, 1=subtract, 2=multiply): ";
        std::cin >> op;
    } while (op < 0 || op > 2);
 
    // Create a function pointer named pFcn (yes, the syntax is ugly)
    int (*pFcn)(int, int) = nullptr;
 
    // Set pFcn to point to the function the user chose
    switch (op)
    {
        case 0: pFcn = add; break;
        case 1: pFcn = subtract; break;
        case 2: pFcn = multiply; break;
    }
 
    // Call the function that pFcn is pointing to with x and y as parameters
    // This uses late binding
    std::cout << "The answer is: " << pFcn(x, y) << std::endl;
 
    return 0;
}

In this example, instead of calling the add(), subtract(), or multiply() function directly, we’ve instead set pFcn to point at the function we wish to call. Then we call the function through the pointer. The compiler is unable to use early binding to resolve the function call pFcn(x, y) because it can not tell which function pFcn will be pointing to at compile time!

在这个例子中,不直接调用这个加减乘函数,我们把这个pFcn指针指向我们想要调用速度函数。然后我们用指针来调用这个函数。编译器没办法使用早绑定来解析这个函数调用pFcn(x, y),因为它不能知道应该把pFcn指向哪一个函数

Late binding is slightly less efficient since it involves an extra level of indirection. With early binding, the CPU can jump directly to the function’s address. With late binding, the program has to read the address held in the pointer and then jump to that address. This involves one extra step, making it slightly slower. However, the advantage of late binding is that it is more flexible than early binding, because decisions about what function to call do not need to be made until run time.

与早绑定相比,迟绑定有一点点低效率,因为它引入了额外的间接的过程。在早绑定的情况下,CPU可以直接跳转到函数的地址,但是在吃绑定的情况下,程序要读到函数指针里面的地址,才能跳到那个地址继续执行,这在中间加了一步,让它变的慢了一点。然而迟绑定的优势也是非常明显的。它带来了更多的灵活性。因为对于要调用哪个函数的决定可以推迟到运行时。

In the next lesson, we’ll take a look at how late binding is used to implement virtual functions.

在下一节,我们会看一下在虚函数中,迟绑定是怎么应用的。