我们从一个具体的问题入手:
“现在有一堆按钮,以及一堆电器,按钮对它需要控制的对象一无所知,电器也不知道它们开关的具体类型,它们之间的关系可能是一对多,也可能是多对一,并且需要支持动态添加和删除,应该如何设计这个结构?”
这里有个形象的图:
为了实现组件间的控制,我们很容易想到“回调函数”,对于C++开发者,我们肯定不希望一个类自身的处理函数存在于类外,但是类成员函数中被自动添加的隐形this形参造成了函数指针调用的不匹配,于是我们想到了使用static成员函数:
// 被调类
class Tv
{
public:
static void onBtnClicked(bool b)
{
if ( b == true )
cout << "Tv is being turned on.";
else
cout << "Tv is being turned off.";
}
};
// 还有其他可能被调用的对象
// class Lamp...
// class Laptop...
typedef void (*PF)(bool);
class Button
{
public:
//主调函数
void click(PF p, bool b)
{
p(b);
}
};
int main()
{
PF p = &Tv::onBtnClicked;
Button btn;
btn.click(p, true);
return 0;
}//被调类
class Tv
{
public:
Tv(int t1, int t2) : bootTime(t1), offTime(t2){}
//被调函数
void onBtnClicked(bool b)
{
if ( b == true )
cout << "Tv is being turned on. bootTime is " << bootTime;
else
cout << "Tv is being turned off. offTime is " << offTime;
}
private:
int bootTime;
int offTime;
};
// 还有其他可能被 Button 类控制的被调类
class Lamp
{
public:
void onBtnClicked()
{
cout << "This Lamp is control by voice";
}
};
// class Laptop...
// 主调类
template<typename Tobject, typename Tparam>
class Button
{
typedef void (Tobject::*Tfunc)(Tparam);
public:
Button(Tobject* pInstance, Tfunc p)
{
m_pInstance = pInstance;
m_pf = p;
}
//主调函数
void click(Tparam p)
{
(m_pInstance->*m_pf)(p);
}
private:
Tfunc m_pf;
Tobject* m_pInstance;
};
int main()
{
Tv tv1(20, 40);
Button<Tv, bool> btn(&tv1, &Tv::onBtnClicked);
btn.click(true);
return 0;
}int main()
{
Tv *tv1 = new Tv(20, 40);
Button<Tv, bool> btn(tv1, &Tv::onBtnClicked);
delete tv1;
btn.click(true);
return 0;
}危险的是,这是一个NULL指针调用成员函数的问题,很多时候编译器并不对该情况报错,于是我们得到了一个诡异的结果——打印了一个随机的bootTime值。有时这种情况藏得很隐蔽,它可能将你拖入调试bug的沼泽。
之所以出现这样的情况,是因为Tv类的完全独立性,它没有能力将自身被delete的信息告诉Button,从而导致Button对其固执地调用;而Button类过多地关注被调对象与其函数指针也依然存在耦合,了解了问题所在,我们再来尝试新的结构。首先我们把Button类中负责调用其他函数的内容剥离出来,用一个新的类Connection表示:
template<typename Tparam>
class Connection_Base
{
public:
virtual MySlot* getdest() const = 0;
virtual void emitSignal(Tparam) = 0;
virtual ~Connection_Base(){}
};
template<typename Tobject, typename Tparam>
class Connection : public Connection_Base<Tparam>
{
typedef void (Tobject::*Tfunc)(Tparam);
public:
Connection(Tobject* pobject, Tfunc p) {
m_pobject = pobject;
m_pf = p;
}
void emitSignal(Tparam parm) {
(m_pobject->*m_pf)(parm);
}
MySlot* getdest() const {
return m_pobject;
}
private:
Tobject* m_pobject;
Tfunc m_pf;
};接下来,为了让“电器们”在销毁时能够及时解除它身上所有的绑定,最直接的方式是通知所有的主调类将它删除。我们可以设计一个父类来完成这个工作,拥有槽函数的类只需要继承它就好:
class MySlot
{
private:
typedef std::set<_Signal_Base*> sender_set;
typedef sender_set::const_iterator const_iterator;
public:
void signal_connect(_Signal_Base* sender) {
m_senders.insert(sender);
}
void signal_disconnect(_Signal_Base* sender) {
m_senders.erase(sender);
}
virtual ~MySlot() {
disconnect_all();
}
void disconnect_all()
{
const_iterator it = m_senders.begin();
const_iterator itEnd = m_senders.end();
while(it != itEnd)
{
(*it)->slot_disconnect(this);
++it;
}
m_senders.erase(m_senders.begin(), m_senders.end());
}
private:
sender_set m_senders;
};有了联结者和槽,接下来我们就该实现Signal了:
class _Signal_Base
{
public:
virtual void slot_disconnect(MySlot* pslot) = 0;
};template<typename Tparam>
class Signal_Base : public _Signal_Base
{
public:
typedef std::list<Connection_Base<Tparam>*> connections_list;
~Signal_Base() {
disconnect_all();
}
void disconnect_all()
{
typename connections_list::const_iterator it = m_connected_slots.begin();
typename connections_list::const_iterator itEnd = m_connected_slots.end();
while(it != itEnd)
{
(*it)->getdest()->signal_disconnect(this);
delete *it;
++it;
}
m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
}
void disconnect(MySlot* pclass)
{
typename connections_list::iterator it = m_connected_slots.begin();
typename connections_list::iterator itEnd = m_connected_slots.end();
while(it != itEnd)
{
if((*it)->getdest() == pclass)
{
delete *it;
m_connected_slots.erase(it);
pclass->signal_disconnect(this);
return;
}
++it;
}
}
void slot_disconnect(MySlot* pslot)
{
typename connections_list::iterator it = m_connected_slots.begin();
typename connections_list::iterator itEnd = m_connected_slots.end();
while(it != itEnd)
{
typename connections_list::iterator itNext = it;
++itNext;
if((*it)->getdest() == pslot)
{
m_connected_slots.erase(it);
// delete *it;
}
it = itNext;
}
}
protected:
connections_list m_connected_slots;
};template<typename Tparam>
class MySignal : public Signal_Base<Tparam>
{
public:
template<typename Tobject>
void connect(Tobject* pclass, void (Tobject::*pmemfun)(Tparam))
{
Connection<Tobject, Tparam>* conn = new Connection<Tobject, Tparam>(pclass, pmemfun);
Signal_Base<Tparam>::m_connected_slots.push_back(conn);
pclass->signal_connect(this);
}
void operator()(Tparam p)
{
typename std::list<Connection_Base<Tparam>*>::const_iterator itNext, it = Signal_Base<Tparam>::m_connected_slots.begin();
typename std::list<Connection_Base<Tparam>*>::const_iterator itEnd = Signal_Base<Tparam>::m_connected_slots.end();
while(it != itEnd)
{
itNext = it;
++itNext;
(*it)->emitSignal(p);
it = itNext;
}
}
};一切就绪,写段测试代码看看:
class Button
{
public:
MySignal<bool> click;
void nowClick(bool b)
{
click(b);
}
};
class Tv : public MySlot
{
public:
Tv(int i) : m_id(i){}
void onButtonClicked(bool b)
{
if ( b == true )
cout << "Tv " << m_id << " is being turned on. " << endl;
else
cout << "Tv "<< m_id <<" is being turned off. " << endl;
}
private:
int m_id;
};
int main()
{
Button btn;
Tv *tv1 = new Tv(1);
Tv *tv2 = new Tv(2);
btn.click.connect(tv1, &Tv::onButtonClicked);
btn.click.connect(tv2, &Tv::onButtonClicked);
delete tv1;
btn.nowClick(false);
return 0;
}可能你已经注意到了,如果我们的信号有两个参数呢?是的,我们需要再写一个有两个参数的Signal_Base,Connection和MySignal。。。如果我们分别为0-8个参数写了9个版本,并为这其中的容器操作加上线程安全机制,那么我们现在完成的就是一份sigslot库。
不知不觉篇幅有点长了~我们就留到下篇再来探讨下,Qt中信号槽的实现与sigslot又有哪些差异?以及moc和QObject又给信号槽机制带来了怎样的灵活性~
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/cloud_castle/article/details/48197123