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