饿汉模式就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
思路:简单来说,就是将类的构造函数,拷贝构造函数,赋值运算符重载私有,随后定义一个静态的类对象,再给出一个静态的类对象的获取方法。
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton()
{
cout<<"Create Singleton Obj."<<endl;
}
// C++98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static Singleton m_instance;
};
Singleton Singleton::m_instance;
//饿汉模式
void main()
{
Singleton *ps = Singleton::GetInstance();
Singleton *ps1 = Singleton::GetInstance();
Singleton *ps2 = Singleton::GetInstance();
system("pause");
}
饿汉模式优点:简单。 但由于只要程序启动,就会初始化类对象,比较耗资源,非线程安全。
思路:简单来说,就是将类的构造函数,拷贝构造函数,赋值运算符重载私有,和饿汉区别在于,随后定义的是一个静态的类对象指针,再给出一个静态的类对象的获取方法,这样保证了只有在定义一个类对象的时候才会真正的初始或这个类。
//不是线程安全
class Singleton
{
public:
static Singleton* Instance()
{
if (_instance == nullptr)
{
_instance = new Singleton;
}
return _instance;
}
protected:
Singleton()
{
cout<<"Create Singleton Obj."<<endl;
}
private:
Singleton(){};
// 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static Singleton* _instance;
};
Singleton* Singleton::_instance = nullptr;
void thread_fun(int n)
{
for(int i=0; i<n; ++i)
{
Singleton *ps = Singleton::Instance();
cout<<"ps = "<<ps<<endl; //一样
}
}
void main()
{
system("pause");
Singleton *pst = Singleton::Instance();
Singleton *pst1 = Singleton::Instance();
Singleton *pst2 = Singleton::Instance();
Singleton *pst3 = Singleton::Instance();
}
但以上程序在多线程情况下是不安全的,要想到到线程安全,可以加锁,同时我们使用双检机制提高效率。
双检机制意味着当显出判断_instance != nullptr)时,说明对象已经被实例,直接返回该对象。
mutex mt;
//不是线程安全
class Singleton
{
public:
static Singleton* Instance()
{
if(_instance == nullptr) //双检锁 //双检机制
{
mt.lock();
if (_instance == nullptr)
{
_instance = new Singleton;
}
mt.unlock();
}
return _instance;
}
protected:
Singleton()
{
cout<<"Create Singleton Obj."<<endl;
}
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = nullptr;
void thread_fun(int n)
{
for(int i=0; i<n; ++i)
{
Singleton *ps = Singleton::Instance();
cout<<"ps = "<<ps<<endl; //一样
}
}
void main()
{
thread t1(thread_fun, 10);
thread t2(thread_fun, 10);
t1.join();
t2.join();
}
但其实我们在实际应用中,并不太会采用以上的做法,我们会定义一个单例的模板类,随后用该模板类和我们要创建的类实例化一个特定的类对象。这个模板类其实我和我们上面实现的单例类是一样的,只是将其变为模板(这个模板类目前还不是线程安全,要想达到线程安全,就必须和我们刚才讲的一样,加锁)。
template<typename T>
class LASingletonTemplateBase
{
private:
static T* sm_instance;
protected:
LASingletonTemplateBase()
{
assert(sm_instance == 0);
sm_instance = static_cast<T*>(this);
}
virtual ~LASingletonTemplateBase()
{
assert(sm_instance != 0);
sm_instance = 0;
}
public:
static T* get_instance_ptr()
{
if (sm_instance == 0)
{
sm_instance = new T();
}
return sm_instance;
}
static T& get_instance_ref()
{
if (sm_instance == 0)
{
sm_instance = new T();
}
return *sm_instance;
}
static void remove_instance()
{
assert(sm_instance);
if (sm_instance)
{
delete sm_instance;
}
assert(sm_instance == 0);
}
};
template<typename T>
T* LASingletonTemplateBase<T>::sm_instance = 0;
class Test
{};
void main()
{
Test t1, t2, t3;
Test *pt1 = LASingletonTemplateBase<Test>::get_instance_ptr();
Test *pt2 = LASingletonTemplateBase<Test>::get_instance_ptr();
Test *pt3 = LASingletonTemplateBase<Test>::get_instance_ptr();
}
其实这个原理我们提供继承一个父类来实现一个类不能拷贝。一般来说,如果我们要实现一个类不能被拷贝,可以讲类的拷贝构造函数和赋值运算符重载私有或者删除。但是这样做比较麻烦,当我们需要多个类不能被拷贝的话,在每个类中将其拷贝构造函数和赋值运算符重载私有或者删除是非常麻烦的。而通过继承一个不能被拷贝的父类就可以一步实现。
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable(const noncopyable&);
const noncopyable& operator=(const noncopyable&);
};
class Test : public noncopyable
{
public:
Test(int data = 0) :m_data(data)
{}
Test(const Test &t)
{
m_data = t.m_data;
}
private:
int m_data = 0;
};
class Test1 : public noncopyable
{
};
void main()
{
Test t1;
Test t2 = t1; //
Test t3;
t3 = t1;
}