优雅的方法,在第一次调用后断开槽连接

11

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

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
}

我希望在第一次调用后断开这个插槽。

问题是:是否有一种方法可以仅调用一次插槽?而无需显式断开连接?像单发射方法一样?这可行吗?


2
使用 disconnect() 函数有什么问题? - vahancho
你为什么不想使用disconnect呢?是有特定的原因吗? - Robert
只是想知道是否还有其他方法。对于一些开发人员来说,在第一次调用后调用断开连接很烦人,我想知道是否有其他方法可以做到这一点。 - Etore Marcari Jr.
我有一个没有参数的信号/槽解决方案,明天我会尝试找到任何参数的解决方案。你有C++11/Qt5吗? - Armaghast
实际上我的环境是Linux,使用C++ 4.4.7和Qt4.8。但是欢迎提供其他解决方案,包括Windows/C++11/Qt5。 - Etore Marcari Jr.
1个回答

12

核心思想是创建一个包装器,一个特殊的“连接”,它会自动断开信号连接。如果您使用了大量的“仅调用一次”连接,则这非常有用;否则,我建议在槽函数的开头使用Qobject :: disconnect。

此实现通过创建两个连接来工作:一个是“普通”的连接,另一个是连接并在之后断开和清理所有内容。

一种实现方式(使用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没有connect to function,因此我正在使用一个包装器:

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的构造函数设置为私有,因为该实例自毁(delete this

对于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的指针。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接