单例模式就是某个类只可以有一个对象/实例,所以我们需要将它的构造函数设置为私有,通过一个函数来返回对象。但我们知道,调用成员函数必须通过对象调用,这就陷入了一个先有鸡还是先有蛋的问题。故我们可以通过静态函数返回一个实例,同时需要将拷贝构造函数和拷贝赋值运算符设置为删除。
返回引用
//懒汉式
class Singleton
{
public:
static Singleton& getInstance(int i_)
{
static Singleton instance(i_);
return instance;
}
~Singleton() = default;
//重载了输出运算符,必须设置为友元函数
friend ostream& operator<<(ostream& out, const Singleton& rhs) {
out << rhs.i;
}
private:
Singleton(int i_):i(i_) {};
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
int i;
};
int main() {
Singleton &t = Singleton::getInstance(10);
Singleton &t1 = Singleton::getInstance(11);
std::cout << t << std::endl; //10
std::cout << t1 << std::endl; //10
}
以上是懒汉式单例模式,只有在需要的时候才会调用getInstance生成对象,同时构造对象的语句用static修饰,确保只会执行一次。由于类的静态变量和函数内的局部静态变量的初始化是线程安全的,故这个单例模式也是线程安全的,不需要额外加锁,这也是《effective c++》最为推荐的单例模式实现形式。
下面的饿汉式的实现:
class Singleton
{
public:
static Singleton& getInstance()
{
return instance;
}
~Singleton() = default;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
//静态变量
static Singleton instance;
};
饿汉式的实现比较简单, 只要定义一个静态变量即可, 但是它的缺点是不管你用不用得到这个单例, 它都会在程序初始化时被创建出来, 有可能会造成资源的浪费。
返回指针
返回指针的实现思路和以上的类似,下面是饿汉版的实现,在main函数执行之前就完成了静态对象的初始化。
class Singleton
{
public:
static Singleton* instance;
static Singleton* getInstance()
{
return instance;
}
~Singleton() = default;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = new Singleton();
接下来是懒汉版本的实现,为了确保单例模式正常执行,需要加锁。
class Singleton
{
public:
static Singleton* p;
static std::mutex mtx;
static Singleton* getInstance()
{
//减少线程对互斥锁的竞争
if(p == nullptr) {
lock_guard<mutex> lock(mtx);
//确保只有一个对象
if(p == nullptr) {
p = new Singleton();
}
}
return p;
}
~Singleton() = default;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};