Каковы преимущества позднего связывания? Приведите один пример в контексте указателей функций в C++

Во-первых, позвольте мне пояснить это вопрос не объясняет ясно мое сомнение. Чтобы контекст был ясным. Я задаю этот вопрос конкретно в отношении указателей функций в C/C++.

Я знаю разницу между ранним связыванием и поздним связыванием и знаю, как это работает. Я хотел бы понять следующее с одним примером использования указателей функций в C/C++:

Во многих учебниках упоминается:

Преимущество позднего связывания заключается в том, что оно более гибкое, чем раннее связывание, поскольку решения о том, какую функцию вызывать, не нужно принимать до момента выполнения.

Также в нем упоминаются:

При позднем связывании программа должна прочитать адрес, содержащийся в указателе, и затем перейти к этому адресу. Это включает в себя один дополнительный шаг, что делает его немного медленнее.

#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;

Может ли кто-нибудь объяснить на простом примере, как показано ниже, где поздняя привязка выгодна и почему кто-то должен выбрать ее, а не раннюю привязку?


person Vivek Vijayan    schedule 05.08.2016    source источник
comment
Одним из примеров является добавление плагина во время выполнения. Как насчет того, чтобы встроить электронную таблицу Excel в документ Word и иметь возможность редактировать ее из Word. Фактически, вы могли бы изобрести формат документа, приложение для его редактирования, а Word мог бы позднее привязаться к вашей программе, и люди могли бы его использовать. Это примеры позднего связывания. Видите ли вы преимущество или нет, это зависит от вас.   -  person clarasoft-it    schedule 05.08.2016
comment
Я спрашиваю конкретно об указателях функций в C/C++. Может ли кто-нибудь снять блокировку и попытаться ответить?   -  person Vivek Vijayan    schedule 06.08.2016
comment
Что заставляет вас думать, что позднее связывание связано с function pointers in C/C++? Это не. См., например, Как использовать Visual C++ для доступа к DocumentProperties с помощью автоматизации — это C++ код, автоматизирующий MS Word с использованием позднего связывания, и function pointer не используется и не требуется.   -  person dxiv    schedule 06.08.2016
comment
@dxiv Я полностью с тобой согласен. Но я пытаюсь понять это через язык программирования, который знаю. Я думал, что это будет легко объяснить с помощью указателей на функции. Не так ли? Слишком сложно объяснить в контексте использования указателей на функции?   -  person Vivek Vijayan    schedule 06.08.2016
comment
@VivekVijayan Все еще не уверен, что вы имеете в виду под in the context of using function pointers. Например, позднее связывание в Windows OLE/COM сводится к IDispatch::GetIDsOfNames и IDispatch:: Вызов. Оба не используют указатели на функции, и я не знаю, как их объяснить in the context of using function pointers. Вам нужно будет лучше сформулировать свой вопрос (и, возможно, более узко), поскольку совсем не ясно, чего вы хотите.   -  person dxiv    schedule 06.08.2016
comment
@dxiv Это редактирование проясняет ситуацию? Я просто имел в виду, что указатели на функции можно использовать для демонстрации позднего связывания. Виртуальные функции также являются хорошими примерами полиморфизма позднего связывания/времени выполнения.   -  person Vivek Vijayan    schedule 06.08.2016
comment
@VivekVijayan Пример кода, который вы только что отредактировали, я бы назвал косвенным вызовом через указатель функции. Я никогда не видел его под названием late binding. Возможно, вам следует упомянуть, что textbooks вы имеете в виду, так как я не думаю, что это слишком распространенное употребление. Что касается самого вопроса, предположим, что вы заменили задачу выполнить операцию над nX, nY и вернуть результат на округлить nX, nY до ближайших кратных 100, выполнить операцию, затем округлить результат до ближайшего кратного из 100. Попробуйте написать последний с указателем на функцию, а не без него — первый будет намного проще и понятнее.   -  person dxiv    schedule 06.08.2016
comment
@VivekVijayan en.wikipedia.org/wiki/Late_binding   -  person kfsone    schedule 06.08.2016


Ответы (1)


Хорошо, я собираюсь пропустить весь вопрос определения «раннее связывание против позднего связывания» и притворюсь, что вы спросили «зачем кому-то использовать указатели на функции вместо оператора switch?»

Потому что указатели функций более гибкие. Они не являются статическими. Давайте рассмотрим бизнес-конец вашего кода:

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 был частью какой-то библиотеки, у вас не обязательно было бы право его изменять. При статической привязке у вас будет негибкая система; он поддерживает то, что будет всегда поддерживать.

С помощью динамического связывания вы можете добавлять все, что хотите, независимо от того, имеете ли вы право напрямую изменять библиотеку или нет.

person Nicol Bolas    schedule 06.08.2016