- 信号槽机制的原理
信号槽是观察者模式的一种实现,或者说是一种升华: - 一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知;
- 一个槽就是一个观察者,通常就是在被观察的对象发生改变的时候——也可以说是信号发出的时候——被调用的函数;
- 你可以将信号和槽连接起来,形成一种观察者-被观察者的关系;
- 当事件或者状态发生改变的时候,信号就会被发出;同时,信号发出者有义务调用所有注册的对这个事件(信号)感兴趣的函数(槽);
- 信号和槽是多对多的关系,一个信号可以连接多个槽,而一个槽也可以监听多个信号;
信号槽机制是Qt的一大特性,Qt信号与槽机制降低了Qt对象的耦合度。但这种做法效率低,不灵活。
- 使用c++实现简单的信号槽机制
(1)信号模板类实现:
// CSignal.hpp
#ifndef _CSIGNAL_H
#define _CSIGNAL_H
#include <vector>
#include <memory>
#include <functional>
#include "CSlot.hpp"
#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))
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
(2)槽模板类实现
// CSlot.hpp
#ifndef _CSLOT_H
#define _CSLOT_H
#include <vector>
#include <functional>
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;
};
#endif
(3)具体信号执行类
#ifndef _SIGNAL_EXECUTER_H
#define _SIGNAL_EXECUTER_H
#include <iostream>
#include "CSignal.hpp"
class SignalExecutor
{
public:
void start()
{
emit signal_print();
emit signal_print_string("emit signal: signal_print_string!");
emit signal_print_strings(100, "emit signal: signal_print_string!");
}
signals:
// 不带参数的信号
Signal<> signal_print;
// 带参数的信号
Signal<std::string> signal_print_string;
// 带两个参数的信号
Signal<int, std::string> signal_print_strings;
};
# endif //_SIGNAL_EXECUTER_H
(4)槽函数具体实现类
#ifndef _SLOT_EXECUTER_H
#define _SLOT_EXECUTER_H
#include <iostream>
#include "CSignal.hpp"
class SlotExecutor
{
public slots:
void slot_print()
{
std::cout << "slot_print:" << std::endl;
}
void slot_print_string(const std::string& str)
{
std::cout << "slot_print_string:" << str << std::endl;
}
void slot_print_strings(int n, const std::string& str)
{
std::cout << "slot_print_strings, param 1:" << n << "======"
<< "param 2:" << str << std::endl;
}
};
# endif //_SLOT_EXECUTER_H
(5)信号槽连接具体实现
#include <iostream>
#include "CSignal.hpp"
#include "slot_executer.h"
#include "signal_executer.h"
void print_string_public(const std::string& str)
{
std::cout << "slot print_string_public:" << str << std::endl;
}
int main(int argc, char *argv[])
{
SignalExecutor signal_executer;
SlotExecutor slot_executer;
// 无参数信号与无参数槽绑定
connect(&signal_executer, signal_print
, std::bind(&SlotExecutor::slot_print, &slot_executer));
// 单参数信号与单参数槽绑定
connect(&signal_executer, signal_print_string
, std::bind(&SlotExecutor::slot_print_string
, &slot_executer, std::placeholders::_1));
// 两个参数信号与两个参数槽绑定
connect(&signal_executer, signal_print_strings
, std::bind(&SlotExecutor::slot_print_strings
, &slot_executer, std::placeholders::_1, std::placeholders::_2));
// 单参数信号与全局函数绑定
connect(&signal_executer, signal_print_string
, std::bind(print_string_public, std::placeholders::_1));
// 单参数信号与lambda绑定
connect(&signal_executer, signal_print_string
, [](const std::string& str){
std::cout << "lambda str: " << str << std::endl;
});
signal_executer.start();
std::cout << "signal slot demo finish!" << std::endl;
system("pause");
}