日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++11?寫一個只觸發一次槽函數的Qt?connect函數_C 語言

作者:manxisuo ? 更新時間: 2022-11-03 編程語言

引言

在之前的Qt項目中,我發現經常會用到槽函數只需要執行一次的情況。也就是說,槽函數執行一次后,就需要disconnect對應的連接。然而,真正操作起來實際上挺麻煩的,或者說不優雅。

因為你需要把之前connect時產生的QMetaObject::Connection對象保存起來,而保存它不能用局部變量,通常需要保存到類的成員變量中,或者其他生命周期足夠長的地方,以防止在disconnect它的時候,它已經失效了。

總之,需要使用者自己維護,因而增加了使用者的負擔。

如果有一個方法能夠在槽函數執行完成后自動disconnect掉連接就好了。我在網上找了一段時間,卻沒有找到合適的解決方案,相關討論也比較少,可能這不是一個很常見的需求吧。不過還是在GitHub上找到了一個相關的庫:https://github.com/misje/once,但是看它的源碼,感覺比較復雜。

它針對QObject::connect函數的每種情況,寫了對應的實現,總感覺太復雜了,應該存在一種更通用的方法。

最近在閱讀了《C++ Primer》模板相關的章節后,我突然想到也許用完美轉發相關的東西可以簡化實現。于是試著寫了一下,貌似真得可以,代碼如下:

ConnectionUtil.h:

#pragma once
#include <QObject>
#include <QMap>
namespace ConnectionUtil
{
    typedef QMetaObject::Connection Conn;
    struct ConnKey {};
    extern QMap<ConnKey*, QPair<Conn, Conn>> connMap;
    class ReceiverObj : public QObject
    {
        Q_OBJECT
    public:
        explicit ReceiverObj(ConnKey *connKey) : key(connKey) {}
    public slots:
        void slot();
    private:
        ConnKey *key;
    };
    // 處理信號為SIGNAL(...)的情況
    template <typename Sender, typename ...Args>
    void connect(Sender &&sender, const char *signal, Args &&...args)
    {
        ConnKey *connKey = new ConnKey;
        Conn conn1 = QObject::connect(std::forward<Sender>(sender), signal, std::forward<Args>(args)...);
        Conn conn2 = QObject::connect(std::forward<Sender>(sender), signal, new ReceiverObj(connKey), SLOT(slot()));
        connMap.insert(connKey, qMakePair(std::move(conn1), std::move(conn2)));
    }
    // 處理其他情況
    template <typename Sender, typename Signal, typename ...Args>
    void connect(Sender &&sender, Signal &&signal, Args &&...args)
    {
        ConnKey *connKey = new ConnKey;
        Conn conn1 = QObject::connect(std::forward<Sender>(sender), std::forward<Signal>(signal), std::forward<Args>(args)...);
        Conn conn2 = QObject::connect(std::forward<Sender>(sender), std::forward<Signal>(signal), [connKey] { ReceiverObj(connKey).slot(); });
        connMap.insert(connKey, qMakePair(std::move(conn1), std::move(conn2)));
    }
}

ConnectionUtil.cpp:

#include "ConnectionUtil.h"
using namespace ConnectionUtil;
QMap<ConnKey *, QPair<Conn, Conn>> ConnectionUtil::connMap;
void ReceiverObj::slot()
{
    const QPair<Conn, Conn> &connections = connMap.value(key);
    QObject::disconnect(connections.first);
    QObject::disconnect(connections.second);
    connMap.remove(key);
    delete key;
    deleteLater();
}

這個實現假定QObject::connect的所有重載函數前兩個參數分別是Sender和Signal,事實上確實是這樣。關鍵點就是,額外建立一個連接,在收到信號后,disconnect用戶的連接。不過,我總感覺這個實現在多線程的情況下可能有bug,但在經過簡單的測試后,暫時沒有發現。

使用示例如下:

ConnectionUtil::connect(this, SIGNAL(bong(int)), obj, SLOT(slotBong(int)), Qt::QueuedConnection);
ConnectionUtil::connect(this, &MainWindow::bong, obj, &SomeObject::slotBong, Qt::QueuedConnection);
ConnectionUtil::connect(this, &MainWindow::bong, this, []() {
    qDebug() << "bingo";
});

原文鏈接:https://segmentfault.com/a/1190000042428429

欄目分類
最近更新