QT信号槽实现-观察者模式架构

前面部分在这里–>(QT信号槽实现原理-观察者设计模式架构-QT源码)

三 、接下来把这些函数连接起来

思路:

信号-槽QObjetc-QMetaObject-实现架构UML图

在这里插入图片描述

  1. 定义两个类: sender(被观察者)和recver(观察者) 均继承于QObject

  2. recver(观察者),定义对某个处理函数比如按钮触发后处理函数。槽函数

  3. sender(被观察者),定义一个数据结构,保持观察者对哪个事件id感兴趣,使用map建立对应关系。并调用(运行时)槽函数。

  4. connect函数在QObject中可由 sender(被观察者)或recver(观察者) 调用

定义一个QObject类,有以下内容:

  1. qt_metacall调用观察者的槽函数用 虚函数重写形式实现 动态绑定实现 观察者模式的关键 在moc分析时将槽函数信号的信息写入 qt_static_metacall函数里 ,此函数在qt_metacall函数动态绑定时调用
  2. 成员变量 QMetaObject staticQtMetaObject;
    QMetaObject 类实现moc, 有以下内容
    关键数据信号和槽的信息都被分别存在此staticQtMetaObject变量里 ,
    相关的宏定义
  3. 数据结构 存放观察者和被观察者信号绑定。std::multimap
  4. connect函数 将观察者和被观察者绑定 利用了上一步数据结构std::multimap进行存储。
对应代码
struct Connection
{
    QObject * receiver;
    int methodID;
};

class QObject
{
public:
...
    static void  connect(Object*, const char*, Object*, const char*);
...
private:
    std::multimap<int, Connection> connections;
 protected:
       static const QMetaObject staticQtMetaObject;
       **int  qt_metacall(QMetaObject::Call _c, int _id, void **_a);** //观察者会对此函数进行重写
    }

QMetaObject类内容:

1、 QMetaObject 里有active(发送者的信号函数里有 QMetaObject::activate,active是被信号函数emit时调用的)
2、 信号和槽函数对应的名称 const char * sig_names;

class QObject;
struct QMetaObject
{
   const char * sig_names;
   const char * slts_names; 
   //信号、槽名称 简化处理 具体内容是  struct d 的 const QByteArrayData *stringdata; (qt_meta_stringdata_XXX.data)

   static void active(QObject * sender, int idx);
};

因为sender(被观察者)需要实现2件事

1 、添加观察者和感兴趣的事件id到容器map 中

程序运行时,connect借助两个字符串,即可将信号与槽的关联建立起来,那么,它是如果做到的呢?

void QObject::connect(Object* sender, const char* sig, Object* receiver, const char* slt)
{
    int sig_idx = find_string(sender->meta.sig_names, sig);//查找信号名在不在 
    int slt_idx = find_string(receiver->meta.slts_names, slt);//查找槽函数在不在
    if (sig_idx == -1 || slt_idx == -1) {
        perror("signal or slot not found!");
    } else {
        Connection c = {receiver, slt_idx};//将receiver sender和对应槽函数id 关联
        **sender->connections.insert(std::pair<int, Connection>(sig_idx, c));**
    }
}

2 、通知事件函数执行逻辑:首先遍历map容器,有没有感兴趣的id
若有,则代表一系列观察者,对这个事件感兴趣,再次遍历观察者列表,让其执行相应的槽函数。
Active实现如下:

void QMetaObject::active(Object* sender, int idx)
{
    ConnectionMapIt it;
    std::pair<ConnectionMapIt, ConnectionMapIt> ret;
    ret = sender->connections.equal_range(idx);
    for (it=ret.first; it!=ret.second; ++it) {
        Connection c = (*it).second;
       c.receiver->qt_metacall(-,c.methodID,-); //索引---槽函数--调用接收槽函数 
    }
}

接下来到观察者这里 调用槽函数

直接调用槽函数我们都知道了,就一个普通函数
可现在通过索引调用了,那么我们必须定义一个接口函数
槽的索引和槽的调用关联起来
前面active通过receiver->qt_metacall调用,所以需要用到 C++的多态,动态多态 重写 虚函数QObject::qt_metacall,

int Worker::qt_metacall(QMetaObject::Call _c, int _id, void **_a)//重写了虚函数 QObject::qt_metacall 实现了运行时调用
{
    _id = QObject::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 2)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 2;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 2)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 2;
    }
    return _id;
}

然后查找槽函数的索引找到槽函数,调用槽函数

void Worker::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Worker *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->resultReady((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        case 1: _t->doWork((*reinterpret_cast< const QString(*)>(_a[1]))); break;// 调用具体操作的槽函数 
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (Worker::*)(const QString & );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Worker::resultReady)) {
                *result = 0;
                return;
            }
        }
    }
}
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在不使用信号的情况下,我们可以使用线程和回调函数来实现观察者模式的异步通信。 具体实现步骤如下: 1. 定义一个观察者类,其中包含一个回调函数指针,用于处理被观察者发出的事件。 2. 定义一个被观察者类,其中包含一个观察者的指针列表,以及一个通知观察者的函数。 3. 在被观察者类中,通知观察者时,使用线程来异步执行通知操作。 4. 在观察者类中,将自己的回调函数指针注册到被观察者的观察者指针列表中。 5. 当被观察者需要通知观察者时,遍历观察者指针列表,异步执行每个观察者的回调函数。 下面是一个简单的示例代码: ``` // 观察者类 class Observer { public: virtual void handleMessage(const QString& message) = 0; }; // 被观察者类 class Subject { public: void addObserver(Observer* observer) { m_observers.append(observer); } void removeObserver(Observer* observer) { m_observers.removeOne(observer); } void sendMessage(const QString& message) { // 使用线程异步执行通知操作 QFuture<void> future = QtConcurrent::run([=]() { for (Observer* observer : m_observers) { observer->handleMessage(message); } }); } private: QList<Observer*> m_observers; }; // 具体的观察者类 class ConcreteObserver : public Observer { public: void handleMessage(const QString& message) override { qDebug() << "Received message: " << message; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建观察者和被观察者对象 ConcreteObserver observer; Subject subject; // 注册观察者的回调函数到被观察者的观察者指针列表中 subject.addObserver(&observer); // 发送消息 subject.sendMessage("Hello World!"); return a.exec(); } ``` 在上面的示例代码中,当被观察者需要通知观察者时,使用线程异步执行通知操作,遍历观察者指针列表,异步执行每个观察者的回调函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值