有下列幾種方式能夠鏈接到信號上c++
qt5將繼續支持舊的語法去鏈接,在QObject對象上定義信號和槽函數,及任何繼承QObjec的對象(包含QWidget)。異步
connect(sender, SIGNAL (valueChanged(QString,QString)),receiver, SLOT (updateValue(QString)) );
下面是一種新的方式來鏈接兩個QObjects:socket
connect(sender, &Sender::valueChanged,receiver, &Receiver::updateValue );
它支持:ide
它不支持:函數
新語法甚至能鏈接到函數,不單單是QObjects:測試
connect(sender, &Sender::valueChanged, someFunction);
支持:ui
能和tr1::bind一塊兒使用:this
connect(sender, &Sender::valueChanged, tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
能和c++ 11 lambda表達式一塊兒使用:線程
connect(sender, &Sender::valueChanged, [=](const QString &newValue) { receiver->updateValue("senderValue", newValue); });
不支持:指針
當receiver被銷燬時,新語法不能自動斷開信號和槽的鏈接。 由於它是沒有跟QObject一塊兒的僞函數。無論怎樣,從5.2版本開始有一個重載函數,它添加一個上下文對象,當對象摧毀時,這個鏈接會破壞。這個上下文也被使用在線程關聯性上: 這個lambda方法將會被調用在對象事件循環的線程中。
如你可能預期的那樣,在qt5中如何終止鏈接也會有一些新變化。
你仍能夠舊方式斷開鏈接(使用SIGNAL, SLOT方式)。但僅限是
disconnect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue );
這隻能夠用在你使用一樣方式的鏈接上,或者你也可使用0做爲通配。 在實際中,它也不適用於靜態函數,仿函數,或lambda函數。
QMetaObject::Connection m_connection; //… m_connection = QObject::connect(…); //… QObject::disconnect(m_connection);
這適用於全部場景下,包括lambda函數和僞函數。
隨着C++ 11,它能夠保持代碼內聯
void doYourStuff(const QByteArray &page) { QTcpSocket *socket = new QTcpSocket; socket->connectToHost("qt.io", 80); QObject::connect(socket, &QTcpSocket::connected, [socket, page] () { socket->write(QByteArray("GET " + page + "")); }); QObject::connect(socket, &QTcpSocket::readyRead, [socket] () { qDebug()<< "GOT DATA "<< socket->readAll(); }); QObject::connect(socket, &QTcpSocket::disconnected, [socket] () { qDebug()<< "DISCONNECTED "; socket->deleteLater(); }); QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)> (&QAbstractSocket::error), [socket](QAbstractSocket::SocketError) { qDebug()<< "ERROR " << socket->errorString(); socket->deleteLater(); }); }
下面是一個不用從新進入事件循環的QDialog,保持代碼屬於:
void Doc::saveDocument() { QFileDialog *dlg = new QFileDialog(); dlg->open(); QObject::connect(dlg, &QDialog::finished, [dlg, this](int result) { if (result) { QFile file(dlg->selectedFiles().first()); // … } dlg->deleteLater(); }); }
使用QHttpServer的另外例子。
用GCC測試的、
幸運的是,IDE能簡化函數的命名,好比Qt Creator。
#include <QtCore> class Goo : public QObject { Goo() { connect(this, &Goo::someSignal, this, &QObject::deleteLater); } signals: void someSignal(); }; qobject.h: In member function 'void QObject::qt_check_for_QOBJECT_macro(const T&&) const [with T = Goo]': qobject.h:535:9: instantiated from 'static typename QtPrivate::QEnableIf<((int) (QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int) (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(), Func2 = void (QObject::*)(), typename QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int) (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = QObject]' main.cc:4:68: instantiated from here qobject.h:353:5: error: void value not ignored as it ought to be make: '''* [main.o] Error 1
#include <QtCore> class Goo : public QObject { Q_OBJECT public: Goo() { connect(this, &Goo::someSignal, this, &Goo::someSlot1); //error connect(this, &Goo::someSignal, this, &Goo::someSlot2); //works } signals: void someSignal(QString); public: void someSlot1(int); void someSlot2(QVariant); }; qobject.h: In static member function 'static typename QtPrivate::QEnableIf<((int) (QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int) (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(QString), Func2 = void (Goo::*)(int), typename QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int) (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = Goo]': main.cc:6:62: instantiated from here qobject.h:538:163: error: no type named 'IncompatibleSignalSlotArguments' in 'struct QtPrivate::CheckCompatibleArguments<QtPrivate::List<QString, void>, QtPrivate::List<int, void>, true>' qobject.h: In static member function 'static void QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::call (QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function, Obj*, void*) [with Args = QtPrivate::List<QString, void>, Obj = Goo, Ret = void, Arg1 = int, QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function = void (Goo::*)(int)]': qobject.h:501:13: instantiated from 'void QObject::QSlotObject<Func, Args>::call(QObject*, void**) [with Func = void (Goo::*)(int), Args = QtPrivate::List<QString, void>, QObject = QObject]' main.cc:14:2: instantiated from here qobject.h:109:13: error: cannot conver
若是你有相似下面的代碼:
class A : public QObject { Q_OBJECT public slots: void someSlot(int foo = 0); };
舊的鏈接方式容許你鏈接這個槽到信號上,不用帶參數。 可是我不能從模板代碼中知道一個函數是否帶有默認參數。所以這個功能是被禁用的。 這裏有個實現方法是,若是槽函數中參數數量多於信號函數中的參數數量時,退回到舊方式去鏈接。 無論怎樣,這是至關不一致的,所以舊語法再也不執行類型類型檢查和類型轉換。 它已經從分支中移除,並被合併。
如你在上面例子中看到的那樣,鏈接到QAbstractSocket::error,它不是真正完美的方式,由於error有一個重載。取得一個重載函數的地址須要隱式轉換。好比一個鏈接以下所示:
connect(mySpinBox, SIGNAL(valueChanged(int)), mySlider, SLOT(setValue(int));
它不能簡單的轉換到:
connect(mySpinBox, &QSpinBox::valueChanged, mySlider, &QSlider::setValue);
由於 QSpinBox有2個信號,名字都叫valueChanged()帶有不一樣的參數。 新方式要用下列代碼替代:
connect(mySpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), mySlider, &QSlider::setValue);
最好的方式是儘可能不要重載信號和槽。
可是咱們已經在過去的release版本中添加劇載了,由於取得函數的地址不是咱們支持的使用方式。 可是如今不破壞代碼兼用性已是不可能的。
是否QMetaObject::Connection應該有一個disconnect()函數?
其餘的難題是,若是咱們使用新語法,在一些對象關閉時,不能自動斷開鏈接。一個方式是在斷開鏈接中添加對象的集合,或者一個新函數例如QMetaObject::Connection::require
auto c = connect(sender, &Sender::valueChanged, [=](const QString &newValue) { receiver->updateValue("senderValue", newValue); } , QList<QObject> { receiver } ); // solution 1 c.require(receiver); // solution 2
方案2是否有效? 沒有什麼比得上QMetaObject::Connection::require()
函數例如QHostInfo::lookupHost或QTimer::singleShot或QFileDialog::open 帶有一個QObject接收者和 char* 的slot。 這在新方式中是不能用的。 若是你想用c++方式的回調,應該使用 std::function (or tr1)。但咱們不能在咱們的API中,使用STL類型,所以一個qt函數應該被完成當複製一個std::function時。 不管如何,這是和QObject鏈接是不相關的。