• 4.3. 连接 Connections

    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 可以显著简化连接的管理。