简体   繁体   中英

“Smart” pointers in C++ 98

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM