C++11 写一个只触发一次槽函数的Qt connect函数

 更新时间:2022年09月09日 11:28:33   作者:manxisuo  
这篇文章主要为大家介绍了C++11 写一个只触发一次槽函数的Qt connect函数实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

在之前的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";
});

以上就是C++11 写一个只触发一次槽函数的Qt connect函数的详细内容,更多关于C++11 Qt connect函数的资料请关注脚本之家其它相关文章!

相关文章

  • VC++ 中ListCtrl经验总结

    VC++ 中ListCtrl经验总结

    这篇文章主要介绍了VC++ 中ListCtrl经验总结的相关资料,需要的朋友可以参考下
    2015-06-06
  • C++ Qt开发之使用QHostInfo查询主机地址

    C++ Qt开发之使用QHostInfo查询主机地址

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,本文将重点介绍如何运用QHostInfo组件实现对主机地址查询功能,希望对大家有所帮助
    2024-03-03
  • C语言中的pause()函数和alarm()函数以及sleep()函数

    C语言中的pause()函数和alarm()函数以及sleep()函数

    这篇文章主要介绍了C语言中的pause()函数和alarm()函数以及sleep()函数,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C++如何将运行结果保存到txt中

    C++如何将运行结果保存到txt中

    这篇文章主要介绍了C++如何将运行结果保存到txt中问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 推箱子游戏C语言实现代码

    推箱子游戏C语言实现代码

    这篇文章主要为大家详细介绍了推箱子游戏C语言实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • C语言超详细讲解指针的概念与使用

    C语言超详细讲解指针的概念与使用

    本文主要讲解C语言中指针和字符串的关系以及指针和数组的关系,在看本文之前大家可以先看看博主之前的C语言基础篇,先对C语言指针先有个基础的了解,有助于对本文章有更深一步的了解
    2022-05-05
  • C++实现String类的方法详解

    C++实现String类的方法详解

    在C语言中,没有专门用来表示字符串的类型。虽然C语言为字符串提供了一系列的库函数,但这些函数与字符串这个类型是分开的。所以在C++中封装了一个string类,来帮助我们操作字符串,本文就为大家提供了实现String类的方法,需要的可以参考一下
    2022-08-08
  • C++网络编程详细讲解

    C++网络编程详细讲解

    计算机是通过TCP/IP协议进行互联从而进行通信的,为了把复杂的TCP/IP协议隐藏起来,更方便的实现计算机中两个程序进行通信,引出了socket这个概念
    2022-10-10
  • boost.asio框架系列之定时器Timer

    boost.asio框架系列之定时器Timer

    这篇文章介绍了boost.asio框架系列之定时器Timer,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • boost.asio框架系列之socket编程

    boost.asio框架系列之socket编程

    这篇文章介绍了boost.asio框架系列之socket编程,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06

最新评论