Boost :: signal2 — удаление объекта со слотом

Учти это:

#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 является наиболее важным, поскольку мне нужно реализовать шаблон наблюдателя, и очень часто время жизни наблюдателей (слотов) не будет статичным (в течение всего времени, когда приложение работает).

2

Решение

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;
}
3

Другие решения

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, после чего он может уведомить, когда он вызывается.

1

Приводятся очень опасные примеры. Взглянуть:

#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_slot) к connect метод. Это может быть решено, например, с помощью упаковщиков ссылок:

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

Будьте осторожны с шаблонами и типами.

0