I have an EventEmitter
and an EventHandler
object. You add EventHandler*
s to the EventEmitter
's vector of handlers. This let's any object that extends a handler be callable through a common interface via the event emitter.
Now the problem will arise when EventHandler
s decide to destruct without the programmer realizing it (usually copy ctors and = operators) and EventEmitter
will eventually call upon it resulting in a program crash.
The first idea is to supply EventHandler
with a reference to its emitter so it can call a detach function during destruction. But, now we must consider that event emitter decides to die and any time after that the handlers destructor could be called. We just moved the problem along.
This sounds like a very common pointer problem that I wouldn't doubt has been solved in C++11 or boost, but I don't have access to either of those. Is there a generic layout for a smart pointer system that could solve this problem in C++ 98?
Some code to illustrate
// 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(&handler1);
// Uh oh, attached, then out of scope
{
Handler handler2("handler2");
emitter.Attach(&handler2);
}
emitter.Emit();
}
Output
Calling a handler
handler1
Calling a handler
Ok this is what I've come up with. A shared pointer that has a ref count. Two objects, well, share access to this pointer. When they die, if the lower the ref count to 0, they will delete the shared pointer. It should not be possible for this to happen more than once. At least in my tests.
// 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 && 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 && 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 && 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(&h_scope_1);
Handler h_scope_2("h_scope_2");
emitter.Attach(&h_scope_2);
{
Handler h_scope_3("h_scope_3");
emitter.Attach(&h_scope_3);
}
emitter.Emit();
}
}
Output
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.