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

目录
  • 引言
    • ConnectionUtil.h:
    • ConnectionUtil.cpp:

引言

在之前的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函数的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++中strlen函数的三种实现方法

    目录 一.strlen函数是什么 二.strlen的三种实现方法 1.第一种方法(直接) 2.第二种方法(递归) 3.第三种方法(指针-指针) 四.小结 一.strlen函数是什么 我们经常用到strlen这个函数求字符串长度,但是它是怎么实现的呢?接下来让给我用三种方法带你们看看它是如何实现? 首先我们先来了解一下strlen这个函数,strlen 是求字符串长度的函数,它的返回值是size_t,就是unsigned int.字符串以'\0'作为结束标志,strlen函数返回的值就是在字符串中

  • C++详细讲解常用math函数的用法

    目录 1.fabs(double x) 2.floor(double x)ceil(double x) 3.pow(double x,double n) 4.sqrt(double x) 5.log(double x) 6.sin(double x)cos(double x) tan(double x) 7.round(double x) 包含头文件 #include<cmath> 1.fabs(double x) 对double型变量取绝对值 #include<iostream>

  • C++关于构造函数可向父类或者本类传参的讲解

    前面我们学习了C++使用初始化列表的方式来初始化字段的方法: https://www.jb51.net/article/153032.htm 这一节的原理和前面的差不多. 在C++的构造函数中,子类继承父类,那么,在创建一个子类成员时,可以同时向父类或者子类的构造函数进行传参,实现方法如下: 写一个例子:mul_argc.c #include <iostream> #include <cstring> using namespace std ; //英雄联盟类 class Hero

  • C++结构体作为函数参数传参的实例代码

    具体代码如下所示: #include<iostream> using namespace std; #include<string> //结构体 struct Student { string name; int age; int score; }st3; /* *结构体作为函数参数传参 */ //值传递 void printStufdent1(struct Student st3) { cout << "子函数" << endl; st

  • C++实现softmax函数的面试经验

    目录 背景 第一次实现 实现 测试 test 1 test 2 第二次实现(改进) 改进原理 实现 测试 test 1 test 2 完整代码 背景 今天面试字节算法岗时被问到的问题,让我用C++实现一个softmax函数.softmax是逻辑回归在多分类问题上的推广.大概的公式如下: 即判断该变量在总体变量中的占比. 第一次实现 实现 我们用vector来封装输入和输出,简单的按公式复现. vector<double> softmax(vector<double> input)

  • C/C++ 函数原理传参示例详解

    目录 x84-64的寄存器 函数是个什么东西? 一个简单的函数 传参姿势 入栈规则 看看汇编 全都存寄存器吗? 传对象呢? x84-64的寄存器 本文所用gcc为 x86-64 gcc 10.1 wiki.cdot.senecacollege.ca/wiki/X86_64… rax - register a extended rbx - register b extended rcx - register c extended rdx - register d extended rbp - re

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

    目录 引言 ConnectionUtil.h: ConnectionUtil.cpp: 引言 在之前的Qt项目中,我发现经常会用到槽函数只需要执行一次的情况.也就是说,槽函数执行一次后,就需要disconnect对应的连接.然而,真正操作起来实际上挺麻烦的,或者说不优雅. 因为你需要把之前connect时产生的QMetaObject::Connection对象保存起来,而保存它不能用局部变量,通常需要保存到类的成员变量中,或者其他生命周期足够长的地方,以防止在disconnect它的时候,它已经

  • 使用Vue写一个datepicker的示例

    前言 写插件是很有意思,也很锻炼人,因为这个过程中能发现许多的细节问题.在前端发展的过程中,jQuery无疑是一个重要的里程碑,围绕着这个优秀项目也出现了很多优秀的插件可以直接使用,大大节省了开发者们的时间.jQuery最重要的作用是跨浏览器,而现在浏览器市场虽不完美,但已远没有从前那么惨,数据驱动视图的思想倍受欢迎,大家开始使用前端框架取代jQuery,我个人比较喜欢Vue.js,所以想试着用Vue.js写一个组件出来. 为了发布到npm上,所以给项目地址改名字了,但是内部代码没有改,使用方法

  • 用函数模板,写一个简单高效的 JSON 查询器的方法介绍

    JSON可谓是JavaScript的亮点,它能用优雅简练的代码实现Object和Array的初始化.同样是基于文本的数据定义,它比符号分隔更有语义,比XML更简洁.因此越来越多的JS开发中,使用它作为数据的传输和储存. JS数组内置了不少有用的方法,方便我们对数据的查询和筛选.例如我们有一堆数据: 复制代码 代码如下: var heros = [        // 名============攻=====防=======力量====敏捷=====智力====        {name:'冰室女巫

  • javascript如何用递归写一个简单的树形结构示例

    现在有一个数据,需要你渲染出对应的列表出来: var data = [ {"id":1}, {"id":2}, {"id":3}, {"id":4}, ]; var str="<ul>"; data.forEach(function(v,i){ str+="<li><span>"+v.id+"</span></li>&

  • vue用Object.defineProperty手写一个简单的双向绑定的示例

    前言 上次写了一个Object.defineProperty() 不详解,文末说要写用它来写个双向绑定.说话算话,说来就来 前文链接 Object.defineProperty() 不详解 先看最后效果 model演示.gif 什么是双向绑定? 1.当一个对象(或变量)的属性改变,那么调用这个属性的地方显示也应该改变,模型到视图(model => view) 2.当调用属性的这个地方改变了这个属性(通常是一个表单元素),那么这个对象(或变量)的属性也会改为最新的值 ,即视图到模型(view =>

  • 详解如何用VUE写一个多用模态框组件模版

    对于新手们来说,如何写一个可以多用的组件,还是有点难度的,组件如何重用,如何传值这些在实际使用中,是多少会存在一些障碍的,所以今天特意写一个最常用的模态框组件提供给大家,希望能帮助到您! 懒癌患者直接复制粘贴即可 Modal.vue组件 <template> <!-- 过渡动画 --> <transition name="modal-fade"> <!-- 关闭模态框事件 和 控制模态框是否显示 --> <div class=&qu

  • 如何用原生js写一个弹窗消息提醒插件

    1.分析 当消息被触发的时候,会有一个自上而下的淡入过程. 在持续了一段时间后会自动的消失,或者是需要用户来手动的点击关闭按钮. 在消息消失的时候,会有一个自下而上的淡出过程. 消息是可以叠加弹出的,最新的消息会排在消息列表的最后面. 当前面的消息消失后,后面的消息会有一个向上滑动效果. 然后消息本身是有三部分组成 消息图标,用来区分不同类型的消息. 消息文本. 关闭按钮,并不是所有消息都需要关闭按钮. 2. 实现样式 那么,不管我们是用原生js还是vue,首先呢,我们都需要把这个消息的基本样式

  • 十行代码使用Python写一个USB病毒

    大家好,我又回来了. 昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun)到.但是,如果我想,比如,当一个usb插入时,在后台自动把usb里的重要文件神不知鬼不觉地拷贝到本地或者上传到某个服务器,就需要特殊的软件辅助. 于是我心想,能不能用python写一个程序,让它在后台运行.每当有u盘插入的时候,就自动拷贝其中重要文件. 如何判断U盘的插入与否? 首先我们打开电脑终

  • 使用Vue如何写一个双向数据绑定(面试常见)

    1.原理 Vue的双向数据绑定的原理相信大家也都十分了解了,主要是通过 Object对象的defineProperty属性,重写data的set和get函数来实现的,这里对原理不做过多描述,主要还是来实现一个实例.为了使代码更加的清晰,这里只会实现最基本的内容,主要实现v-model,v-bind 和v-click三个命令,其他命令也可以自行补充. 添加网上的一张图 2.实现 页面结构很简单,如下 <div id="app"> <form> <input

  • 用vue写一个日历

    之前在上家公司做过一个公司人员考勤的东西,里面需要用到日历,当时自己用vue随便写了一个,比较简单,删掉了其他功能的代码,只留下日历,直接看代码 <template> <div class="lookForMonth_wrapper"> <div class="lookForMonth_top"> <div class="selectDate"> <div>{{year}} 年 {{mo

随机推荐