Недостатки обратного вызова - PullRequest
2 голосов
/ 29 апреля 2011

От: http://doc.qt.nokia.com/4.7/signalsandslots.html

Обратные вызовы имеют два фундаментальных недостатка: во-первых, они не являются типобезопасными.Мы никогда не можем быть уверены, что функция обработки будет вызывать обратный вызов с правильными аргументами.

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

РЕДАКТИРОВАТЬ 1 Как указывает Gui13 в приведенном ниже сообщении, QString выдает ошибку, когда вместо нее передается символ *.Но я протестировал следующую программу:

#include <iostream>
#include <QString>
#include <stdio.h>
typedef int (*callback_function)( QString *string);

int MyCallback( std::string string )
{
    if (string.empty() == false)
        std :: cout << string.length();
    return 0;
}

int main ()
{
    /* in another function */
    char *badQstring = (char*)"Booohhh";
    MyCallback( (std::string )badQstring ); 
}

Работает нормально.Означает ли это, что у Qt есть некоторые проблемы с обратными вызовами, и это не значит, что упомянутый выше недостаток также присутствует в простом C ++ или я лаю не на то дерево?

Ответы [ 3 ]

3 голосов
/ 29 апреля 2011

Ну, скажем, Qt хочет, чтобы вы дали ему обратный вызов, который принимает указатель на QString в качестве аргумента: ваша C ++ typedef для обратного вызова будет выглядеть так:

typedef int (*callback_function)( QString *string);

Теперь, когда этот обратный вызоввызывается, вы никогда не можете быть уверены, что переданный аргумент действительно является QString: в C ++ это утверждение допустимо и, скорее всего, приведет к аварийному завершению вашего обратного вызова:

int MyCallback( QString *string )
{
   if(string)
       printf("QString value: %s\n", string->toAscii());
}

/* in another function */
char *badQstring = "Booohhh";
MyCallback( (QString *)badQstring ); // crash, badQstring is not a QString!

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

3 голосов
/ 29 апреля 2011

Пожалуйста, посмотрите на sqlite3_exec() в качестве хорошего примера. Параметр void* является указателем на «объект контекста», который передается в функцию обратного вызова при вызове последней. Пользователь должен быть уверен, что это void* указывает на тип, который он ожидает.

Например, вам нужен некоторый сложный класс в качестве «объекта контекста». Вы передаете адрес объекта этого класса в sqlite3_exec(), и он неявно преобразуется в void*, затем, когда вызывается ваш обратный вызов, вам нужно привести его обратно из void*, и никто не поймает вас, если вы приведете его к неправильному тип.

0 голосов
/ 29 апреля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...