#c
Вопрос:
У меня есть EventEmitter
и EventHandler
объект. Вы EventHandler*
добавляете EventEmitter
s в вектор обработчиков s. Это позволяет любому объекту, расширяющему обработчик, вызывать его через общий интерфейс через излучатель событий.
Теперь проблема возникнет, когда EventHandler
s решит уничтожить без осознания этого программистом (обычно копируют операторы ctor и=) и EventEmitter
в конечном итоге вызовет его, что приведет к сбою программы.
Первая идея состоит в том, чтобы предоставить EventHandler
ссылку на его излучатель, чтобы он мог вызывать функцию отсоединения во время уничтожения. Но теперь мы должны учитывать, что эмиттер события решает умереть, и в любое время после этого может быть вызван деструктор обработчиков. Мы просто сдвинули проблему с мертвой точки.
Это звучит как очень распространенная проблема с указателями, которая, я бы не сомневался, была решена в C 11 или boost, но у меня нет доступа ни к одному из них. Существует ли общий макет для системы интеллектуальных указателей, который мог бы решить эту проблему в C 98?
Немного кода для иллюстрации
// Example program
#include <iostream>
#include <string>
#include <vector>
class Handler {
public:
std::string msg;
Handler(std::string msg):msg(msg){}
void Run(){
std::cout << msg << std::endl;
}
};
class Emitter {
public:
std::vector<Handler*> handlers;
void Attach(Handler *handler){
handlers.push_back(handler);
}
void Detach(Handler *handler){
// find the handler, remove
}
void Emit(){
for(size_t i = 0; i < handlers.size(); i ){
std::cout << "Calling a handler" << std::endl;
handlers[i]->Run();
}
}
};
int main()
{
Emitter emitter;
Handler handler1("handler1");
emitter.Attach(amp;handler1);
// Uh oh, attached, then out of scope
{
Handler handler2("handler2");
emitter.Attach(amp;handler2);
}
emitter.Emit();
}
Выход
Calling a handler
handler1
Calling a handler
Комментарии:
1.
auto_ptr
это слабоеunique_ptr
место в старых стандартных библиотеках C . Может быть, все-таки лучше свернуть свой собственный. Это гораздо сложнее, чем кажется на первый взгляд. С учетом сказанного, если вы используете его только для своего конкретного случая использования, а не в общем, это должно быть нормально.2. Именно это я и собираюсь сделать. Просто сделай так, чтобы это работало на этих двух парней. Так что же им делать? Указывать друг на друга? У вас есть какая-то система подсчета ссылок?
3. Интеллектуальные указатели C 11 используют подсчет ссылок. Они даже потокобезопасны (с использованием атомики).
4. реализация boost-это afaik только для заголовков, для этих конкретных модулей нет двоичных файлов. Является ли ваш компилятор или платформа настолько странными, что вы не можете позаимствовать вдохновение из их реализации C 98 shared_ptr или использовать его напрямую? Тогда каковы шансы, что ваша реализация не будет работать или не будет разрешена проверкой кода или будет иметь недостатки в крайнем случае?
5. Хорошая идея! Я посмотрю, смогу ли я интегрировать его.
Ответ №1:
Хорошо, вот что я придумал. Общий указатель, у которого есть счетчик ссылок. Два объекта, ну, разделяют доступ к этому указателю. Когда они умрут, если уменьшить количество ссылок до 0, они удалят общий указатель. Это не должно произойти больше одного раза. По крайней мере, в моих тестах.
// Example program
#include <iostream>
#include <string>
#include <vector>
class SharedPointer {
public:
SharedPointer()
:ref_count(0)
{
}
int ref_count;
};
class Handler {
public:
SharedPointer *shared_pointer;
std::string name;
Handler(std::string name)
:shared_pointer(NULL),
name(name)
{
std::cout << "Constructing " << name << std::endl;
}
~Handler(){
std::cout << "Destructing " << name << std::endl;
// Emitter is still alive
if(shared_pointer amp;amp; shared_pointer->ref_count){
shared_pointer->ref_count--;
if(!shared_pointer->ref_count){
delete shared_pointer;
std::cout << "- - Emitter is dead, so deleting shared ptr " << std::endl;
}
else {
std::cout << "- - Emitter is still alive, so leaving shared ptr " << std::endl;
}
}
}
void Run(){std::cout<<"Running"<<std::endl;}
};
class SmartHandler {
public:
Handler *handler;
SharedPointer *shared_pointer;
SmartHandler(Handler *handler, SharedPointer *shared_pointer)
:handler(handler),
shared_pointer(shared_pointer)
{
handler->shared_pointer = shared_pointer;
handler->shared_pointer->ref_count ;
}
};
class Emitter {
public:
std::vector<SmartHandler> handlers;
~Emitter(){
for(size_t i = 0; i < handlers.size(); i ){
std::cout << "Removing a handler" << std::endl;
if(handlers[i].shared_pointer amp;amp; handlers[i].shared_pointer->ref_count){
handlers[i].shared_pointer->ref_count--;
if(!handlers[i].shared_pointer->ref_count){
delete handlers[i].shared_pointer;
std::cout << "- - Handler is dead, so deleting shared ptr " << std::endl;
}
else {
std::cout << "- - " << handlers[i].handler->name << " is still alive, so leaving shared ptr " << std::endl;
}
}
}
}
void Attach(Handler *handler){
SharedPointer *shared_pointer = new SharedPointer();
shared_pointer->ref_count ;
SmartHandler smart_handler(handler, shared_pointer);
handlers.push_back(smart_handler);
}
void Detach(Handler *handler){
// find the handler, remove
}
void Emit(){
for(size_t i = 0; i < handlers.size(); i ){
if(handlers[i].handler amp;amp; handlers[i].shared_pointer->ref_count > 1){
std::cout << "Calling Run() for handler " << handlers[i].handler->name << std::endl;
handlers[i].handler->Run();
}
else{
std::cout << "This handler appears to be dead" << std::endl;
}
}
}
};
int main()
{
Handler h_scope_1("h_scope_1");
{
Emitter emitter;
emitter.Attach(amp;h_scope_1);
Handler h_scope_2("h_scope_2");
emitter.Attach(amp;h_scope_2);
{
Handler h_scope_3("h_scope_3");
emitter.Attach(amp;h_scope_3);
}
emitter.Emit();
}
}
Выход
Constructing h_scope_1
Constructing h_scope_2
Constructing h_scope_3
Destructing h_scope_3
- - Emitter is still alive, so leaving shared ptr
Calling Run() for handler h_scope_1
Running
Calling Run() for handler h_scope_2
Running
This handler appears to be dead
Destructing h_scope_2
- - Emitter is still alive, so leaving shared ptr
Removing a handler
- - h_scope_1 is still alive, so leaving shared ptr
Removing a handler
- - Handler is dead, so deleting shared ptr
Removing a handler
- - Handler is dead, so deleting shared ptr
Destructing h_scope_1
- - Emitter is dead, so deleting shared ptr