后期绑定有什么好处?在 C++ 中的函数指针上下文中举一个例子

What are the advantages of late binding? Give one example in context of function pointers in C++

首先,让我澄清一下this问题没有把我的疑问解释清楚。明确上下文。我特别针对 C/C++.

中的函数指针问这个问题

我知道早期绑定和后期绑定的区别及其工作原理。我想了解的是以下一个使用 C/C++ 中的函数指针的示例:

在很多教科书上都提到过:

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.

另外,它提到:

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.

#include <iostream>
using namespace std;

int Add(int nX, int nY)
{
    return nX + nY;
}

int Subtract(int nX, int nY)
{
    return nX - nY;
}

int Multiply(int nX, int nY)
{
    return nX * nY;
}

int main()
{
    int nX;
    cout << "Enter a number: ";
    cin >> nX;

    int nY;
    cout << "Enter another number: ";
    cin >> nY;

    int nOperation;
    do
    {
        cout << "Enter an operation (0=add, 1=subtract, 2=multiply): ";
        cin >> nOperation;
    } while (nOperation < 0 || nOperation > 2);

    // Create a function pointer named pFcn (yes, the syntax is ugly)
    int (*pFcn)(int, int);

    // Set pFcn to point to the function the user chose
    switch (nOperation)
    {
        case 0: pFcn = Add; break;
        case 1: pFcn = Subtract; break;
        case 2: pFcn = Multiply; break;
    }

    // Call the function that pFcn is pointing to with nX and nY as parameters
    cout << "The answer is: " << pFcn(nX, nY) << endl;

    return 0;
}

在这里,使用后期绑定没有任何优势,应该首选下面示例中的早期绑定。

int nResult = 0;
switch (nOperation)
{
    case 0: nResult = Add(nX, nY); break;
    case 1: nResult = Subtract(nX, nY); break;
    case 2: nResult = Multiply(nX, nY); break;
}

cout << "The answer is: " << nResult << endl;

有人可以用一个像下面这样的简单示例来解释,其中 后期绑定是有利的,为什么有人应该选择后期绑定而不是早期绑定?

好的,我将跳过整个 "early binding vs late binding" 定义问题并假装你问 "why would someone use function pointers instead of a switch statement?"

因为函数指针更加灵活。它们不是 静态的。 让我们来看看代码的业务端:

int InvokeOperation(int nOperation, int nX, int nY)
{
    switch (nOperation)
    {
        case 0: return Add(nX, nY);
        case 1: return Subtract(nX, nY);
        case 2: return Multiply(nX, nY);
    }
}

很好,很实用。但它并不灵活。为什么?因为所有可以调用的函数都是由InvokeOperation定义的;如果你想添加一个新的操作,你必须能够改变 InvokeOperation.

相比之下,如果使用函数指针,则可以构建整个操作注册表:

using Func = int(*)(int, int);
struct Op{Func func; std::string name;};
std::vector<Func> funcs =
{
    {&Add, "Add"},
    {&Subtract, "Subtract"},
    {&Multiply, "Multiply"},
};

int InvokeOperation(int nOperation, int nX, int nY)
{
    return funcs[nOperation].func(nX, nY);
}

现在,如果要添加更多操作,只需将元素插入 funcs。如果 InvokeOperation 是某个图书馆的一部分,您不一定有权更改它。使用静态绑定,您将拥有一个不灵活的系统;它支持的是它将始终支持的。

使用动态绑定,您可以添加任何您想要的东西,无论您是否有权直接修改库。