4.3. 连接 Connections

函数可以通过由 boost::signal 所提供的 connect()disconnect() 方法的帮助来进行管理。 由于 connect() 会返回一个类型为 boost::signals::connection 的值,它们可以通过其它方法来管理。

  1. #include <boost/signal.hpp>
  2. #include <iostream>
  3.  
  4. void func()
  5. {
  6. std::cout << "Hello, world!" << std::endl;
  7. }
  8.  
  9. int main()
  10. {
  11. boost::signal<void ()> s;
  12. boost::signals::connection c = s.connect(func);
  13. s();
  14. c.disconnect();
  15. }

boost::signaldisconnect() 方法需要传入一个函数指针,而直接调用 boost::signals::connection 对象上的 disconnect() 方法则略去该参数。

除了 disconnect() 方法之外,boost::signals::connection 还提供了其它方法,如 block()unblock()

  1. #include <boost/signal.hpp>
  2. #include <iostream>
  3.  
  4. void func()
  5. {
  6. std::cout << "Hello, world!" << std::endl;
  7. }
  8.  
  9. int main()
  10. {
  11. boost::signal<void ()> s;
  12. boost::signals::connection c = s.connect(func);
  13. c.block();
  14. s();
  15. c.unblock();
  16. s();
  17. }

以上程序只会执行一次 func()。 虽然信号 s 被触发了两次,但是在第一次触发时 func() 不会被调用,因为连接 c 实际上已经被 block() 调用所阻塞。 由于在第二次触发之前调用了 unblock(),所以之后 func() 被正确地执行。

除了 boost::signals::connection 以外,还有一个名为 boost::signals::scoped_connection 的类,它会在析构时自动释放连接。

  1. #include <boost/signal.hpp>
  2. #include <iostream>
  3.  
  4. void func()
  5. {
  6. std::cout << "Hello, world!" << std::endl;
  7. }
  8.  
  9. int main()
  10. {
  11. boost::signal<void ()> s;
  12. {
  13. boost::signals::scoped_connection c = s.connect(func);
  14. }
  15. s();
  16. }

因为连接对象 c 在信号触发之前被销毁,所以 func() 不会被调用。

boost::signals::scoped_connection 实际上是派生自 boost::signals::connection 的,所以它提供了相同的方法。它们之间的区别仅在于,在析构 boost::signals::scoped_connection 时,连接会自动释放。

虽然 boost::signals::scoped_connection 的确令自动释放连接更为容易,但是该类型的对象仍需要管理。 如果在其它情形下连接也可以被自动释放,而且不需要管理这些对象的话,就更好了。

  1. #include <boost/signal.hpp>
  2. #include <boost/bind.hpp>
  3. #include <iostream>
  4. #include <memory>
  5.  
  6. class world
  7. {
  8. public:
  9. void hello() const
  10. {
  11. std::cout << "Hello, world!" << std::endl;
  12. }
  13. };
  14.  
  15. int main()
  16. {
  17. boost::signal<void ()> s;
  18. {
  19. std::auto_ptr<world> w(new world());
  20. s.connect(boost::bind(&world::hello, w.get()));
  21. }
  22. std::cout << s.num_slots() << std::endl;
  23. s();
  24. }

以上程序使用 Boost.Bind 将一个对象的方法关联至一个信号。 在信号触发之前,这个对象就被销毁了,这会产生问题。 我们不传递实际的对象 w,而只传递一个指针给 boost::bind()。 在 s() 被实际调用的时候,该指针所引向的对象已不再存在。

可以如下修改这个程序,使得一旦对象 w 被销毁,连接就会自动释放。

  1. #include <boost/signal.hpp>
  2. #include <boost/bind.hpp>
  3. #include <iostream>
  4. #include <memory>
  5.  
  6. class world :
  7. public boost::signals::trackable
  8. {
  9. public:
  10. void hello() const
  11. {
  12. std::cout << "Hello, world!" << std::endl;
  13. }
  14. };
  15.  
  16. int main()
  17. {
  18. boost::signal<void ()> s;
  19. {
  20. std::auto_ptr<world> w(new world());
  21. s.connect(boost::bind(&world::hello, w.get()));
  22. }
  23. std::cout << s.num_slots() << std::endl;
  24. s();
  25. }

如果现在再执行,num_slots() 会返回 0 以确保不会试图调用已销毁对象之上的方法。 仅需的修改是让 world 类继承自 boost::signals::trackable。 当使用对象的指针而不是对象的副本来关联函数至信号时,boost::signals::trackable 可以显著简化连接的管理。