C ++中的单例模式

时间:2010-03-23 01:16:28

标签: c++ singleton

我对单身人士模式有疑问。

我在单例类中看到了两个关于静态成员的案例。

首先它是一个对象,就像这样

class CMySingleton
{
public:
  static CMySingleton& Instance()
  {
    static CMySingleton singleton;
    return singleton;
  }

// Other non-static member functions
private:
  CMySingleton() {}                                  // Private constructor
  ~CMySingleton() {}
  CMySingleton(const CMySingleton&);                 // Prevent copy-construction
  CMySingleton& operator=(const CMySingleton&);      // Prevent assignment
};

一个是指针,就像这个

class GlobalClass
{
    int m_value;
    static GlobalClass *s_instance;
    GlobalClass(int v = 0)
    {
        m_value = v;
    }
  public:
    int get_value()
    {
        return m_value;
    }
    void set_value(int v)
    {
        m_value = v;
    }
    static GlobalClass *instance()
    {
        if (!s_instance)
          s_instance = new GlobalClass;
        return s_instance;
    }
};

这两种情况有什么区别?哪一个是正确的?

8 个答案:

答案 0 :(得分:60)

答案 1 :(得分:4)

两者都不比另一个更正确。我倾向于试图避免一般使用Singleton,但是当我面对认为它是要走的路时,我已经使用了这两个并且它们工作正常。

指针选项的一个故障是它会泄漏内存。另一方面,你的第一个例子可能会在你完成它之前被摧毁,所以如果你不选择为这个东西找出一个更合适的所有者,你就会有一场工资战,在适当的时间创造并销毁它。

答案 2 :(得分:2)

不同之处在于第二个泄漏内存(单身本身)而第一个没有。静态对象在第一次调用它们的关联方法时被初始化,并且(只要程序干净地退出)它们在程序退出之前被销毁。带指针的版本将在程序出口处分配指针,而像Valgrind这样的内存检查器会抱怨。

另外,是什么阻止某人做delete GlobalClass::instance();

由于以上两个原因,使用静态的版本是更常用的方法,也是原始设计模式书中规定的方法。

答案 3 :(得分:1)

使用第二种方法 - 如果你不想使用atexit释放你的对象,那么你总是可以使用keeper对象(例如auto_ptr,或者自写的东西)。在完成对象之前,这可能会导致释放,就像第一个方法一样。

不同之处在于,如果你使用静态对象,你基本上无法检查它是否已经被释放。

如果你使用指针,你可以添加额外的静态bool来指示单例是否已被销毁(如Monoid)。然后你的代码总是可以检查单例是否已被销毁,虽然你可能会失败,但至少你不会得到神秘的“段落故障”或“访问冲突”,程序将避免异常终止。 / p>

答案 4 :(得分:1)

我同意比利的观点。在第二种方法中,我们使用 new 从堆中动态分配内存。除非调用删除,否则此内存始终保持且永远不会被释放。因此,全局指针方法会造成内存泄漏。

class singleton
{
    private:
        static singleton* single;
        singleton()
        {  }
        singleton(const singleton& obj)
        {  }

    public:
        static singleton* getInstance();
        ~singleton()
        {
            if(single != NULL)
            {
                single = NULL;
            }
        }
};

singleton* singleton :: single=NULL;
singleton* singleton :: getInstance()
{
    if(single == NULL)
    {
        single = new singleton;
    }
    return single;
}

int main() {
    singleton *ptrobj = singleton::getInstance();
    delete ptrobj;

    singleton::getInstance();
    delete singleton::getInstance();
    return 0;
}

答案 5 :(得分:0)

你的第一个例子对于单身人士更为典型。您的第二个示例有所不同,因为它是按需创建的。

但是我会尽量避免使用单例,因为它们只不过是全局变量。

答案 6 :(得分:0)

更好的方法是创建单例类。这也避免了GetInstance()函数中的实例可用性检查。这可以使用函数指针来实现。

class TSingleton;

typedef TSingleton* (*FuncPtr) (void);

class TSingleton {

TSingleton(); //prevent public object creation
TSingleton  (const TSingleton& pObject); // prevent copying object
static TSingleton* vObject; // single object of a class

static TSingleton* CreateInstance   (void);
static TSingleton* Instance     (void);
public:

static FuncPtr  GetInstance; 
};


FuncPtr TSingleton::GetInstance = CreateInstance;
TSingleton* TSingleton::vObject;

TSingleton::TSingleton()
{
}

TSingleton::TSingleton(const TSingleton& pObject)
{
}

TSingleton* TSingleton::CreateInstance(void)
{
if(vObject == NULL){

    // Introduce here some code for taking lock for thread safe creation
    //...
    //...
    //...

    if(vObject == NULL){

        vObject = new TSingleton();
        GetInstance = Instance;
    }
}

return vObject;
}

TSingleton* TSingleton::Instance(void)
{

return vObject;

}

void main()
{

TSingleton::GetInstance(); // this will call TSingleton::Createinstance()

TSingleton::GetInstance(); // this will call TSingleton::Instance()

// all further calls to TSingleton::GetInstance will call TSingleton::Instance() which simply returns already created object. 

}

答案 7 :(得分:-1)

针对“内存泄漏”投诉,有一个简单的解决方法:

// dtor
~GlobalClass()
{
    if (this == s_instance)
        s_instance = NULL;
}

换句话说,给类提供一个析构函数,当在程序终止时销毁单例对象时,它会解除隐藏指针变量的初始化。

完成此操作后,这两种形式几乎完全相同。唯一重要的区别是,一个返回对隐藏对象的引用,而另一个返回指向它的指针。

<强>更新

正如@BillyONeal指出的那样,这不起作用,因为指向对象永远不会被删除。哎哟。

我讨厌考虑它,但你可以使用atexit()来做肮脏的工作。啧。

哦,好吧,没关系。