C++11實現Qt的信號槽機制


概述

Qt的信號槽機制是Qt的核心機制,按鈕點擊的響應、線程間通信等都是通過信號槽來實現的,boost里也有信號槽,但和Qt提供的使用接口很不一樣,本文主要是用C++11來實現一個簡單的信號槽,該信號槽也實現了emit、slots、signals、connect關鍵字和函數、使用方法和Qt的信號槽基本類似,該信號槽機制用到了C++11的特性有:

  1. 可變參數模板類
  2. 智能指針
  3. 函數相關std::function、std::bind
  4. using關鍵字
  5. 完美轉發std::forward

該信號槽提供類成員函數、類非成員函數的連接、連接時支持std::bind、以及lambda表達式,信號槽機制的核心代碼如下:

// Connect.hpp
#ifndef _CONNECT_H
#define _CONNECT_H

#include <vector>
#include <memory>
#include <functional>

#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))

template<typename... Args>
class Slot
{
public:
    using OnFunc = std::function<void(Args&&...)>;

    Slot(const OnFunc& func)
        : m_func(func)
    {
        // Do nothing
    }

    void exec(Args&&... args)
    {
        m_func(std::forward<Args>(args)...);
    }

private:
    OnFunc m_func = nullptr;
};

template<typename... Args>
class Signal
{
public:
    using SlotPtr = std::shared_ptr<Slot<Args&&...>>; 
    using OnFunc = std::function<void(Args&&...)>;

    void bind(const OnFunc& func)
    {
        m_slotVec.push_back(SlotPtr(new Slot<Args&&...>(func)));
    }

    void operator()(Args&&... args)
    {
        for (auto& iter : m_slotVec)
        {
            iter->exec(std::forward<Args>(args)...);
        }
    }

private:
    std::vector<SlotPtr> m_slotVec;
};

#endif


下面是使用C++11信號槽機制的代碼:

// main.cpp
/************************************************
 * 該例程講解用C++11來實現Qt的信號槽機制
 * 使用到的C++11特性有:
 * 1.可變參數模板類
 * 2.智能指針
 * 3.函數相關std::function、std::bind
 * 4.using關鍵字
 * 5.完美轉發std::forward
************************************************/
#include "Connect.hpp"
#include <iostream>
#include <string>

class A
{
public:
    void start()
    {
        emit m_s1();
        emit m_s2("Hello C++11");
        emit m_s3(100, "Hello C++11");
    }

signals:
    Signal<> m_s1;  // 不帶參數的信號
    Signal<std::string> m_s2;
    Signal<int, std::string> m_s3;
};

class B
{
public slots:
    void func1()
    {
        std::cout << "func1" << std::endl;
    }

    void func2(const std::string& str)
    {
        std::cout << str << std::endl;
    }

    void func3(int n, const std::string& str)
    {
        std::cout << n << " " << str << std::endl;
    }
};

void func(const std::string& str)
{
    std::cout << "func " << str << std::endl;
}

int main()
{
    A a;
    B b;

    // 信號與槽綁定
    connect(&a, m_s1, std::bind(&B::func1, &b));
    connect(&a, m_s2, std::bind(&B::func2, &b, std::placeholders::_1));
    connect(&a, m_s3, std::bind(&B::func3, &b, std::placeholders::_1, std::placeholders::_2));
    connect(&a, m_s2, std::bind(func, std::placeholders::_1));
    connect(&a, m_s2, [](const std::string& str)
            {
                std::cout << "lambda str: " << str << std::endl;
            });

    a.start();

    return 0;
}


TODO

該例子只是實現了簡單的信號槽機制,還有很多功能都沒有實現

  1. 不支持斷開信號與槽的連接disconnect
  2. 不支持AutoConnection、DirectConnection、QueuedConnection、UniqueConnection
  3. 不支持無鎖連接
  4. etc...

該例子的github地址:https://github.com/chxuan/samples/tree/master/Connect


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM