我是靠谱客的博主 腼腆发卡,最近开发中收集的这篇文章主要介绍sigslot源代码分析解释一下这个UML图代码细节,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述



原文链接:http://my.oschina.net/tianxialangui/blog/67005


对signal/slot机制非常感兴趣,所以网上找了几份实现,不过能力不足,大部分实现感觉有点难度,后来看到sigslot库,这个非常简单,核心代码其实就500来行代码。没有心理压力,所以写一份源代码分析,表示我也看懂了一份工业级别的源代码:

sigslot的uml图片

这是我自己画的一份sigslot的UML图片,对sigslot的源代码做了如下简化:

  1. 消除了signal1...signaln之类的重复代码,这样子就消除了connect1...connectn这些“多余”的代码了。这样简化了很多。
  2. 去除了多线程管理代码,这个在分析源代码的时候没有什么作用。

第二,这个UML我加上了一些自己的东西:

例如图中的has_slots和signal_base,根据UML的规则,这说明has_slots和_signal_base是一对多的关系,即一个has_slots对象可以拥有多个_signal_base对象,我添加的内容是,has_slots和_signal_base的连线,一端指向了has_slots的成员变量m_senders,这说明一个has_slots是通过m_senders这个私有变量来“拥有”多个_signal_base对象的,至于m_senders是std::set类型的,这并不是我们所关心的,所以UML图中并没有表现出来。

解释一下这个UML图

如果理解了sigslot的源代码,看看这个UML图,代码思路是很清晰的。现在还是让我来说明一下这个UML图吧。

为什么需要has_slots类?为了什么有slot的类要继承自has_slots类呢?

因为has_slots实现了对signal对象的管理。我们来看一下如下的代码:

01 class A
02 {
03 public:
04   void f ();
05   void g();
06   void h(int);
07 }
08  
09 signal0<> s1;
10 signal0<> s2;
11 signal1<int> s3;
12 {
13   A a_obj;
14   s1.connect (&a_boj, &A::f);
15   s2.connect (&a_obj, &A::f);
16   s3.connect (&a_obj, &A::h);
17 }
18 s.emit ();

当类A的对象a_obj离开自己的作用域时,a_obj被析构。上面的代码中,类A没有继承自
has_slots,所以类A中的析构函数其实什么也没有做。也就是说,s还是连接中对象a_obj
和&A::f的连接。所以在a_obj的作用域外面调用s.emit()会导致一些问题。

由于signal和slot机制中,signal完全失去对拥有slot对象的控制权,所以,断开连接的操作,应该在类A的析构函数中完成

因为类A对象的 slot 可能连接到了许多不同的signal对象,类A的析构函数中,要自动断开所有这些signal的
连接。所以对象a中需要存储所有signal对象。代码中是采用一个set保存的:

1 sender_set m_senders;

这正是UML图中,has_slots和signal_base的关系为一对多的原因。

关于_signal_base的继承关系

由于C++对可变模板形参没有提供支持,所以库的作者使用signal0,signal1,...,signal8,这类代码的形式来模拟多个可变形参。

对于signaln,它继承自一个_signal_basen。_signal_basen 又继承自_signal_base。_signal_base这个根基类是必须的。

如果没有_signal_base这个根基类,那么上文中的m_senders里面应该保存什么呢?signal0类的指针?signal1类的指针?
明显都不是。m_senders需要保存某个类的指针,这个指针能在运行期运行指定的函数(这个函数会需要断开slot对象和signal对象的连接)。

如上例子,类A的对象a,同时连接了signal0<>的两个对象,还连接了signal1<int>的一个对象。所以当对象a被析构时,需要断开signal0<>和signal1<int>这两个类的连接,这样m_senders中需要保存signal0<>和signal1<int>这两种类型的指针。如果没有_signal_base这个根基类,我们根本就没有办法完成这个需求。

很明显,继承,是解决这个问题的正确漂亮简单的编码方式:

01 class _signal_base
02 {
03     // 这个函数断开p_obj和signal0<>中对象的连接
04     virtual void slot_disconnect (has_slots<> *p_boj) = 0;
05 }
06 class signal0<> : public _signal_base
07 {
08 public:
09     void slot_disconnect (has_slots<> *p_obj);
10 };
11 class signal1<int> : public _signal_base
12 {
13 public:
14     void slot_disconnect (has_slots<> *p_obj);
15 }
16 A::~A ()
17 {
18     for (set_iterator beg = m_senders.begin (); beg != m_senders.end (); ++beg)
19     {
20         // 根据多态的规则,slot_disconnect在运行期会自动选择是
21         // signal0<>的slot_disconnect还是signal1<int>的
22         // slot_disconnect。
23         (*beg)->slot_disconnect (this);
24     }
25 }

_connect_base0和_signal_base0之间的多对一关系


这个关系很容易理解:每一个信号都需要包含多个slot。而slot是通过connect系列的类实现的。signal通过list容器包含了
多个connect_basen对象,实现了signal和slot之间的一对多关系。

signal通过connect系列类调用所连接的对象成员函数。connect实现了slot。

connect_base类的继承关系和connect_base类如何实现slot

要了解connect_base类ruuhe实现slot,首先我们需要了解C++中的成员函数指针。

这里不打算深入讲解C++的成员函数指针,不过希望你能理解如下代码:

01 class A {
02 public :
03   void f();
04   void g ();
05 };
06  
07 typedef void (A::*AMemFunc) ();
08  
09 AMemFunc ptr_mem_func = &A::f;
10 A *a = new A;
11 a->*ptr_mem_func(); // 调用A::f ()
12 ptr_mem_func = &A::g;
13 a->*ptr_mem_func(); // 调用A::g()

很明显,成员函数指针提供了运行期调用不同函数的功能。

为了实现slot,编译期需要记录类型信息。如下代码是不能通过编译的:

1 A *a;
2 void *p = a;
3 p->*ptr_mem_func() ;// 错误

这就是_connection0模板的作用:

通过_connection0模板,编译器记录了类型信息,使得_connection0的emit函数中才可能正确的调用函数。

不过很可惜,模板实例化出来的每个类都是不相关的,意味着,我们无法使用容器包含一下两个对象:

1 _connection0<A> a;
2 _connection0<B> b;

因为a和b其实是毫无关系的两个对象,所以_connection0需要一个共同的基类:

1 class _connection_base0
2 {
3     virtual void emit () const = 0;
4 }
5 template <typename T>
6 class _connection0 : public _connection_base0
7 {
8     void emit () { ... }
9 };

这就是_connect系列中必须出现继承关系的原因。

代码细节

待续,虽然sigslot的整体框架比较简单,但是代码细节中考虑到的事情还是比较多的。不过对于一个有志向研究sigslot代码的人来说,前面的分析已经足够了。


最后

以上就是腼腆发卡为你收集整理的sigslot源代码分析解释一下这个UML图代码细节的全部内容,希望文章能够帮你解决sigslot源代码分析解释一下这个UML图代码细节所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部