Как использовать обратные вызовы в C++?
Я пытаюсь использовать обратные вызовы в C++, используя std::function
, У меня есть два файла, mainwindow.cpp
а также tcpclient.cpp
, Функция-член mainwindow
передается tcpclient
в порядке вызова переданной функции, когда происходит определенное событие.
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
void connectedToServer(int errorCode);
~MainWindow();
private slots:
void on_connectButton_clicked();
TCPClient *tcpClient_;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpClient_ = &TCPClient("localhost", ui->portText->text(), ui->consoleText, this->connectedToServer)
}
tcpclient.h
#ifndef TCPCLIENTH
#define TCPCLIENTH
#include <QTcpSocket>
#include <QString>
#include <QJsonDocument>
#include <QTextEdit>
#include <functional>
#include <tcpclientbadresponse.h>
#include <tcpclientserverdisconnected.h>
class TCPClient : public QTcpSocket
{
public:
TCPClient(QString hostName, int port, QString clientID, QTextEdit *consoleText,
std::function<void(int)> *onCompletionCallback);
void connectToServer(QString requestType, QJsonDocument requestJson);
QJsonDocument getResponse() const;
private:
std::function<void(int)> onCompletionCallback;
};
#endif // TCPCLIENTH
tcpclient.cpp
#include "tcpclient.h"
#include <QDebug>
#include <QIODevice>
#include <QAbstractSocket>
#include <QByteArray>
#include <exception>
TCPClient::TCPClient(QString hostName, int port, QString clientID, QTextEdit *consoleText,
std::function<void(int)> *onCompletionCallback)
: hostName_(hostName),
port_(port),
clientID_(clientID),
consoleText_(consoleText),
onCompletionCallback(onCompletionCallback)
{
this->connectionStatus_ = TCPClient::CONNECTION_STATUS::IDLE;
qDebug() << "Client " << clientID_ << " created";
}
Я получаю следующую ошибку
должна быть вызвана нестатическая функция-член
Что означает эта ошибка? Как я использую std::function
пройти обратные вызовы?
РЕДАКТИРОВАТЬ: Согласно первому комментарию, я удалил части кода, не связанные с этим вопросом, чтобы улучшить ясность.
2 ответа
Помимо ответа Сэма Варшавачика, решение ошибки - сделать void onCompletionCallback(int)
static
функция-член
В показанном коде есть несколько проблем. По крайней мере, две проблемы очевидны: одна, которая вызывает ошибку компиляции, и вторая, с ошибкой компиляции в стороне, приведет к неопределенному поведению и в значительной степени гарантированному падению.
std::function
член класса объявляется следующим образом:
std::function<void(int)> onCompletionCallback;
Это объявление кажется правильным, но попытка инициализировать этого члена класса выглядит следующим образом:
TCPClient::TCPClient(/* ... */
std::function<void(int)> *onCompletionCallback)
: /* ... */
onCompletionCallback(onCompletionCallback)
Параметр для конструктора является указателем на это std::function
и сделана попытка инициализировать это std::function
от указателя на std::function
,
Это не скомпилируется по той же самой причине, по которой вы не сможете скомпилировать все, что объявлено как
int someClassmember;
А потом инициализируется как
someConstructor(int *someClassmember)
: someClassmember(someClassmember)
Вы не можете инициализировать int
от указателя на int
, Точно так же вы не можете инициализировать std::function
от указателя на std::function
, Исправление должно быть очевидным. Параметр конструктора не должен быть указателем; и, оптимально, это должно быть объявлено как const std::function<...> ¶meter
, const
ссылка.
Вторая проблема заключается в MainWindow
конструктор:
tcpClient_ = &TCPClient( /* ... */ )
Это выражение "TCPClient( ...)" создает временный объект. Этот временный объект уничтожается сразу после вычисления полного выражения.
Это приводит к тому, что это выражение сохраняет указатель на временный объект, который немедленно уничтожается. Последующее разыменование этого указателя приводит к дикому разыменованию указателя и почти гарантированному падению. Как только вы исправите свою ошибку компиляции, вы обнаружите, что ваш код будет ужасно падать, пока вы тоже не исправите это.
Большинство современных компиляторов достаточно умны, чтобы обнаружить этот частый вид логической ошибки и выдать предупреждение. Если ваш компилятор также выдает предупреждение в этой строке, это ценный урок, чтобы не игнорировать диагностику компилятора, даже если компилятор все еще компилирует полученный код. Предупреждающие сообщения компиляторов почти всегда выдаются по уважительной причине и не должны игнорироваться.