3

考虑一下:

#include <boost/signals2.hpp>
#include <iostream>

struct object_with_slot
{
void operator()()
{
   std::cout << "Slot called!" << std::endl;
   member = 50500;
}
int member;
};


int main()
{
boost::signals2::signal<void ()> sig;

object_with_slot * ptr = new object_with_slot;
sig.connect(*ptr);

delete ptr;

sig();
}

输出是“插槽调用!” 并且没有崩溃或任何事情。这就是为什么我有几个问题:

1)为什么没有崩溃?

2)为什么即使槽函数将某些东西分配给不存在的对象,也没有崩溃?

3)如何使信号自动跟踪其插槽的生命周期?我的意思是当插槽被破坏时,它会断开连接。

第 3 个问题是最重要的,因为我需要实现观察者模式,并且观察者(插槽)的生命周期通常不会是静态的(在应用程序运行的整个时间内)。

4

3 回答 3

3

1)你很幸运。如果没有,您将遇到分段错误。

2) 内存没有以任何方式被覆盖。

3)您可以使用 slot::track 在被跟踪对象被删除时自动断开连接。Boost.Signals2 可以跟踪由 boost::shared_ptr 管理的对象。

#include <boost/signals2.hpp>
#include <boost/shared_ptr.hpp>

struct object_with_slot
{
    void operator()()
    {
       std::cout << "Slot called!" << std::endl;
       member = 50500;
    }
    int member;
};

//
int main()
{
    typedef boost::signals2::signal<void ()> sig_type;
    sig_type sig;

    {
        boost::shared_ptr<object_with_slot> ptr(new object_with_slot);
        sig.connect(sig_type::slot_type(*ptr).track(ptr));

        // 'object_with_slot' managed by ptr is destroyed
    }

    sig(); // 'object_with_slot' not called here.

    return 0;
}

更新:
添加了跟踪 std::shared_ptr 和 std::weak_ptr 对象的代码:

#include <memory>
#include <boost/signals2.hpp>

// added specializations for std::weak_ptr and std::shared_ptr
namespace boost
{
  namespace signals2
  {
    template<typename T> struct weak_ptr_traits<std::weak_ptr<T> >
    {
      typedef std::shared_ptr<T> shared_type;
    };

    template<typename T> struct shared_ptr_traits<std::shared_ptr<T> >
    {
      typedef std::weak_ptr<T> weak_type;
    };
  }
}

struct object_with_slot
{
    void operator()()
    {
       std::cout << "Slot called!" << std::endl;
       member = 50500;
    }
    int member;
};

//
int main()
{
    typedef boost::signals2::signal<void ()> sig_type;
    sig_type sig;

    std::shared_ptr<object_with_slot> ptr(new object_with_slot);
    sig.connect(sig_type::slot_type(*ptr).track_foreign(ptr)); // ptr is tracked

    sig();

    return 0;
}
于 2013-02-14T20:20:19.410 回答
1

1 和 2) 实际上这是一种未定义的行为。您使用了取消引用运算符,现在 connect 具有 object_with_slot 的值,它的地址可以由内存管理器自由分配给任何其他进程。巧合的是,它仍然是一个“有效地址”。并且 ptr 可以自由地分配给任何其他值而不会导致内存泄漏。

尝试这样的事情,你会看到每次都会爆炸

#include <boost/signals2.hpp>
#include <iostream>

struct object_with_slot
{
    object_with_slot()
    {
        member = new int(10);
    }

    ~object_with_slot()
    {
        delete member; //comment this line and everything works again
    }
    void operator()()
    {
        std::cout << "Slot called!" << std::endl;
        *member = 50500; //it was destroyed above
    }
    int *member;
};


int main()
{
    boost::signals2::signal<void ()> sig;

    object_with_slot * ptr = new object_with_slot;
    sig.connect(*ptr);

    delete ptr;
    ptr = 0x0;

    sig();
}

3)你可以在object_with_slot的析构函数上放另一个信号,然后它可以在调用时通知。

于 2013-02-14T20:15:09.167 回答
0

给出了非常危险的例子。看一看:

#include <iostream>
#include <memory>
#include <boost/signals2.hpp>

struct object_with_slot
{
    object_with_slot() {
        std::cout << "ctor\n";
    }

    object_with_slot(const object_with_slot &) {
        std::cout << "cctor\n";
    }

    ~object_with_slot() {
        std::cout << "dtor\n";
    }

    void operator()()
    {
       std::cout << "Slot called!" << std::endl;
       member = 50500;
    }
    int member;
};

//
int main()
{
    typedef boost::signals2::signal<void ()> sig_type;
    sig_type sig;

    std::shared_ptr<object_with_slot> ptr(new object_with_slot);
    sig.connect(sig_type::slot_type(*ptr).track_foreign(ptr)); // ptr is tracked

    sig();

    return 0;
}

你怎么看,这段代码输出了什么(g++ 4.8.1,libboost 1.54)?

ctor
cctor
cctor
cctor
cctor
cctor
cctor
cctor
cctor
dtor
dtor
dtor
dtor
dtor
cctor
dtor
cctor
dtor
dtor
dtor
cctor
dtor
Slot called!
dtor
dtor

我不认为,这种行为是意料之中的。*ptr因为我们将(实例)的副本(按值)传递object_with_slotconnect方法。例如,它可以通过引用包装器来解决:

sig.connect(sig_type::slot_type(boost::ref(*ptr)).track_foreign(ptr)); // ptr is tracked

小心使用模板和类型。

于 2013-10-03T10:23:37.757 回答