草庐IT

c++ - 第一次通话后断开插槽的优雅方式

coder 2023-11-16 原文

在构造器内部,有一个连接:

connect(&amskspace::on_board_computer_model::self(),
      SIGNAL(camera_status_changed(const amskspace::camera_status_t&)),
      this,
      SLOT(set_camera_status(const amskspace::camera_status_t&)));

方法:

void camera_model::
set_camera_status(const amskspace::camera_status_t& status) {
  disconnect(&amskspace::on_board_computer_model::self(),
             SIGNAL(camera_status_changed(const amskspace::camera_status_t&)),
             this,
             SLOT(set_camera_status(const amskspace::camera_status_t&)));

  // do the job
}

我想在第一次通话后断开这个插槽。

问题是:有没有办法只调用一次插槽?没有明确的断开连接?像单发方法?可能吗?

最佳答案

核心思想是创建一个包装器,一个自动断开信号的特殊“连接”。如果您使用很多“给我打电话一次”连接,这将很有用;否则我建议在插槽的开头使用 Qobject::disconnect。

此实现通过创建 2 个连接来工作:一个“正常”连接,一个会断开连接并立即清理所有连接。

一个实现(使用 C++11/Qt 5,):

template <typename Func1, typename Func2>
static inline QMetaObject::Connection weakConnect(
        typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
        typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot)
{

    QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);

    QMetaObject::Connection* conn_delete = new QMetaObject::Connection();

    *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete](){
        QObject::disconnect(conn_normal);
        QObject::disconnect(*conn_delete);
        delete conn_delete;
    });
    return conn_normal;
}

注意事项/需要改进的地方:

  • 清理发生在调用常规插槽之后。如果常规插槽导致信号再次发射,则常规插槽将再次执行(可能导致无限递归)。
  • 没有正确的断开方式,除非发出信号。 (您可以使用 QObject::disconnect,但这会导致小的内存泄漏)
  • 依赖于槽的执行顺序。目前看来还好。
  • 命名

测试使用:

class A : public QObject
{
    Q_OBJECT
signals:
    void sig(int a);
};


class B : public QObject
{
    Q_OBJECT
public:
    B(int b) : QObject(), b_(b) {}
    int b() const { return b_; }
public slots:
    void slo(int a) { qDebug() << "\tB :" << b_ << "a:" << a;  }
private:
    int b_;
};

A a1;
A a2;

B b10(10);
B b20(20);

weakConnect(&a1, &A::sig, &b10, &B::slo);
weakConnect(&a1, &A::sig, &b20, &B::slo);
weakConnect(&a2, &A::sig, &b20, &B::slo);

qDebug() << "a1 :"; emit a1.sig(1);// Should trigger b10 and b20 slo
qDebug() << "a2 :"; emit a2.sig(2);// Should trigger b20 slo
qDebug() << "a1 :"; emit a1.sig(3);// Should do nothing
qDebug() << "a2 :"; emit a2.sig(4);// Should do nothing

测试代码输出:

a1 :
    B : 10 a: 1
    B : 20 a: 1
a2 :
    B : 20 a: 2
a1 :
a2 :

摆脱 C++11/Qt5(我没有 Qt 4.8/GCC4.4.7,所以没有用它们进行测试) 根据文档,Qt 4.8 没有连接功能,所以我使用包装器:

class ConnectJanitor : public QObject
{
    Q_OBJECT
public slots:
    void cleanup()
    {
        QObject::disconnect(conn_normal_);
        QObject::disconnect(*conn_delete_);
        delete conn_delete_;
        delete this;
    }
public:
    static ConnectJanitor* make(QMetaObject::Connection conn_normal,
                                QMetaObject::Connection* conn_delete)
    {
        return new ConnectJanitor(conn_normal, conn_delete);
    }
private:
    ConnectJanitor(QMetaObject::Connection conn_normal,
                   QMetaObject::Connection* conn_delete) :
        QObject(0) , conn_normal_(conn_normal), conn_delete_(conn_delete) {}

    ConnectJanitor(const ConnectJanitor&); // not implemented
    ConnectJanitor& operator=(ConnectJanitor const&);


    QMetaObject::Connection conn_normal_;
    QMetaObject::Connection* conn_delete_;
};

(我将 ConnectJanitor 的构造函数设为私有(private),因为该实例会自毁(删除此))

对于weakConnect:

static inline QMetaObject::Connection weakConnect(const QObject * sender, const char * signal, const QObject * receiver, const char * slot)
{
    QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
    QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
    *conn_delete = QObject::connect(sender, signal, ConnectJanitor::make(conn_normal, conn_delete), SLOT(cleanup()));
    return conn_normal;
}

如果您需要手动断开连接,我建议让 weakConnect() 返回 ConnectJanitor 的指针。

关于c++ - 第一次通话后断开插槽的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27019290/

有关c++ - 第一次通话后断开插槽的优雅方式的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. ruby-on-rails - Rails 优雅地处理超时 session ? - 2

    使用rails4,ruby2。我在rails配置中为我的cookiesession设置了30分钟的超时时间。问题是,如果我转到表单,让session超时,然后提交表单,我会收到此ActionController::InvalidAuthenticityToken错误。如何在Rails中优雅地处理这个错误?比如说,重定向到登录屏幕? 最佳答案 在您的ApplicationController:rescue_fromActionController::InvalidAuthenticityTokendoredirect_tosome_p

  6. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

  7. ruby-on-rails - 优雅的 Rails : multiple routes, 相同的 Controller Action - 2

    让多条路线去同一条路的最优雅的方式是什么ControllerAction?我有:get'dashboard',to:'dashboard#index'get'dashboard/pending',to:'dashboard#index'get'dashboard/live',to:'dashboard#index'get'dashboard/sold',to:'dashboard#index'这很丑陋。有什么“更优雅”的建议吗?一个类轮的奖励积分。 最佳答案 为什么不只有一个路由和一个Controller操作,并根据传递给它的参数来

  8. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  9. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  10. ruby - 鸭子输入字符串、符号和数组的优雅方式? - 2

    这是针对我无法破坏的现有公共(public)API,但我确实希望对其进行扩展。目前,该方法采用字符串或符号或任何其他在作为第一个参数传递给send时有意义的内容我想添加发送字符串、符号等列表的功能。我可以只使用is_a吗?数组,但还有其他发送列表的方法,这不是很像ruby​​。我将调用列表中的map,所以第一个倾向是使用respond_to?:map。但是字符串也会响应:map,所以这行不通。 最佳答案 如何将它们全部视为数组?String的行为与仅包含String的Array相同:deffoo(obj,arg)[*arg].eac

随机推荐