C++(Singleton)更高效单例模式实现

时间:2022-01-07 16:52:49


单例模式(来自google,了解可以跳过下面文字叙述)

  单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

  实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

  单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。


通常单例模式有两种构建方式:(以下两种方式仅供理解,不推荐使用)

  懒汉方式。指全局的单例实例在第一次被使用时构建。

  饿汉方式。指全局的单例实例在类装载时构建。

懒汉方式:

class GlobalClass
{
int m_value;
public:
GlobalClass(int v = 0)
{
m_value = v;
}
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
};

// Default initialization
GlobalClass *global_ptr = 0;

void foo(void)
{
// Initialization on first use
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(1);
cout << "foo: global_ptr is " << global_ptr->get_value() << '\n';
}

void bar(void)
{
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(2);
cout << "bar: global_ptr is " << global_ptr->get_value() << '\n';
}

int main()
{
if (!global_ptr)
global_ptr = new GlobalClass;
cout << "main: global_ptr is " << global_ptr->get_value() << '\n';
foo();
bar();
}
Output

main: global_ptr is 0
foo: global_ptr is 1
bar: global_ptr is 2


饿汉方式

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;
}
};

// Allocating and initializing GlobalClass's
// static data member. The pointer is being
// allocated - not the object inself.
GlobalClass *GlobalClass::s_instance = 0;

void foo(void)
{
GlobalClass::instance()->set_value(1);
cout << "foo: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
}

void bar(void)
{
GlobalClass::instance()->set_value(2);
cout << "bar: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
}

int main()
{
cout << "main: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
foo();
bar();
}
Output

main: global_ptr is 0
foo: global_ptr is 1
bar: global_ptr is 2

饿汉方式

#include <iostream>
using namespace std;
class mySingletonClass {

static mySingletonClass *mySingletonObject;

mySingletonClass()
{
cout<<"My Solo Object Created\n";
}
public:
static mySingletonClass* getSingletonObject();

};

mySingletonClass* mySingletonClass::mySingletonObject=NULL;
mySingletonClass* mySingletonClass::getSingletonObject(){

if(!mySingletonObject){

mySingletonObject=new mySingletonClass;
}
return mySingletonObject;
}

int main(int argc, const char * argv[])
{

mySingletonClass *myObject;
myObject=myObject->getSingletonObject();
cout<<myObject<<"\n";

///2nd object get the reference of the first object!

mySingletonClass *myAnotherObject;
myAnotherObject=myAnotherObject->getSingletonObject();
cout<<myAnotherObject<<"\n";
return 0;
}
Output:

My Solo Object Created
Memory:X
Memory:X

分析:
懒汉方式:对于用户非常不友好,创建对象都需要用户先通过全局变量判断该类实例是否已经申请,更糟糕是在多线程环境下不安全。需要用户自己手动控制synchronize。最大区别就是懒汉方式是延时初始化(lazy initialization),当用户需要的创建的时候才生成实例。

饿汉方式:在多线程下面是安全的,但是容易泄漏内存。而且不可以延时初始化,就是当该类建立完后,该实例就被创建。


以下提供了一种更为高效方法(推荐)
多线程安全的情况下,而且是可以延时初始化,不容易泄漏内存。(近乎完美)
See this article for a simple design for a lazy evaluated with guaranteed destruction singleton。

#include <iostream>
using namespace std;
class mySingletonClass {

mySingletonClass()
{
cout<<"My Solo Object Created\n";
}
public:
/*
// C++ 03
// ========
// Dont forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
//implement as follow
//private:
//mySingletonClass(mySingletonClass const&) // Don't implement
//void operator=(mySingletonClass const&) // Don't implement
*/
static mySingletonClass& getSingletonObject()
{
static mySingletonClass mySingletonObject; //Guaranteed to be destroyed.
return mySingletonObject;
}
mySingletonClass(mySingletonClass const&) = delete;
void operator=(mySingletonClass const&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status

};
int main(int argc, const char * argv[])
{
mySingletonClass &myObject = myObject.getSingletonObject();
cout<<&myObject<<endl;
mySingletonClass &myAnotherObject = myAnotherObject.getSingletonObject();
cout<<&myAnotherObject<<endl;
return 0;
}
Output

My Solo Object Created
0x47e258
0x47e258

参考:

http://*.com/questions/1008019/c-singleton-design-pattern

https://sourcemaking.com/design_patterns/singleton/cpp/1