Properly deleting a singleton

只愿长相守 提交于 2019-12-20 06:32:36

问题


I have the following code:

MyClass.h:

static MyMutex instanceMutex;
static MyClass* getInstance();
static void deleteInstance();

MyClass.c:

MyMutex MyClass::instanceMutex;

MyClass* MyClass::getInstance()
{   
    if (theInstance == 0)
    {
        instanceMutex.acquire();
        if (theInstance == 0)
        {
            theInstance  = new MyClass();
        }
        instanceMutex.release();
    }
    return theInstance;
}

void MyClass::deleteInstance()
{   
    if (theInstance != 0)
    {
        instanceMutex.acquire();
        if (theInstance != 0)
        {
           theInstance->finalize();
           delete theInstance; 
           theInstance = 0;
        }
        instanceMutex.release();
    }
    return;
}

I have 2 questions on this:

  • Is the above code correct and safe?
  • After I call 'delete theInstance' in MyClass::deleteInstance(), I then call

    • theInstance = 0;
    • instanceMutex.release();

    But if the instance is deleted than how is that even possible? isn't the memory of the class gone?


回答1:


If it's a singleton - it is defined to have exactly one instance - if you delete it - this drops to 0

So it seems you should not support delete at all




回答2:


Here be a problem:

if (theInstance == 0)  // <- Some other thread might see a non-null theInstance 
{                      // before the constructor below returns

    instanceMutex.acquire();
    if (theInstance == 0)
    {
        theInstance  = new MyClass();  // <- This might set theInstance to something 
                                       // before the constructor returns.
    }
    instanceMutex.release();
}

You may want to implement some sort of reference counting system (like using shared_ptr) and initializing it in a similar manner, though taking care to ensure that its instance pointer is not set before the initialization completes.

If you are feeling adventurous, you could also try:

if (theInstance == 0)
{
    instanceMutex.acquire();
    if (theInstance == 0)
    {
        MyClass * volatile ptr = new MyClass();
        __FuglyCompilerSpecificFenceHintThatMightNotActuallyDoAnything();
        theInstance = ptr;  // <- Much hilarity may ensue if this write is not atomic.
    }
    instanceMutex.release();
}

This might fix it, and it might not. In the second case, it depends on how your compiler handles volatile, and weather or not pointer-sized writes are atomic.




回答3:


In one project we inherited from some external company I've seen a whole nightmare class of errors caused by someone deleting the singleton. Perhaps in your rare case deleting the singleton doesn't have any side effects for the whole application, as you can be sure there is no instance of this singleton at use, but, generally, it's an excellent example of bad design. Someone could learn from your example and it would be a bad - even harmful - lesson. In case you need to clean up something in the singleton when the application exit, use the pattern ith returning reference to the singleton, like in example:

#include <iostream>

using namespace std;

class singleton {
    private:
        singleton() {
            cout << "construktor\n";
        }
        ~singleton() {
            cout << "destructor\n";
        }
    public:
        static singleton &getInstance() {
            static singleton instance;
            cout << "instance\n";
            return instance;
        }
        void fun() {
            cout << "fun\n";
        }
};


int main() {
    cout << "one\n";
    singleton &s = singleton::getInstance();
    cout << "two\n";
    s.fun();
    return 0;
}



回答4:


I prefer to implement singletons in C++ in the following manner:

class Singleton
{
public:
    static Singleton& instance()
    {
        static Singleton theInstance;
        return theInstance;
    }

    ~Singleton()
    {
        // Free resources that live outside the processes life cycle here,
        // if these won't automatically be released when the occupying process 
        // dies (is killed)!
        // Examples you don't have to care of usually are:
        // - freeing virtual memory
        // - freeing file descriptors (of any kind, plain fd, socket fd, whatever)
        // - destroying child processes or threads
    }

private:
    Singleton()
    {
    }

    // Forbid copies and assignment
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
};

You can have locking mechanisms also, to prevent concurrent instantiation from multiple threads (will need a static mutex of course!). But deletion is left to the (OS specific) process context here.
Usually you don't need to care about deletion of singleton classes, and how their acquired resources are released, because this is all handled by the OS when a process dies.
Anyway there might be use cases, when you want to have your singleton classes some backup points on program crash situations. Most C++ crt implementations support calling destructors of statically allocated instances, but you should take care not to have ordered dependencies for any of those destructors.




回答5:


getInstance and delteInstance should be static, so they can only work with static members of the class. The static members don't get destroyed with the instance.

If your code is multithreaded, the code is not safe. There's no way to make sure there's no pointer to the instance held in some running context.



来源:https://stackoverflow.com/questions/33451412/valgrind-through-memory-leak-if-singleton-object-is-not-deleted

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!