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