因为在类中只能创建一个对象所以我们要把它的构造函数设置成私有,把它的拷贝构造和赋值重载删除。
构造函数设置成私有:如果是公有的话,用户就可以随便调用来创建对象了。
删除拷贝构造的原因:防止用创建出来的单例对象来拷贝构造一个新的对象。
删除赋值重载的原因:某些对象具体值语义,可以通过赋值产生一个对象副本(系统本身的缺陷,可以把对象赋值给空间)
由于c++要向下兼容于c,所以c++是一个既有值语义,也有对象语义的语言。
对于c++的值语义,大家可以自行去学习。
饿汉式:程序一运行单例对象就被创建好了。
懒汉式:只有当用户想创建时才创建单例对象。
饿汉式
class Singleton
{
private:
Singleton(){}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& GetInstance()
{
static Singleton s;
return s;
}
};
对于饿汉式:在c11后是线程安全的,因为c11规定了在多线程中创建静态变量时,其它线程必须等到创建该变量的线程创建完才能创建。
懒汉式
class Singleton
{
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* GetInstance()
{
if (nullptr == instance_)
{
instance_ = new Singleton();
}
return instance_;
}
private:
class CGarbo
{
public:
~CGarbo()
{
if (nullptr != Singleton::instance_)
{
delete Singleton::instance_;
}
}
};
static Singleton* instance_; //创建这个静态对象,程序结束后系统会调用它的析构函数从而会释放单例对象
static CGarbo cGarbo_;
};
Singleton* Singleton::instance_ = nullptr;
Singleton::CGarbo Singleton::cGarbo_;
这个在多线程场景下不安全,可能会出现多个线程同时判断单例实例为空,从而创建多个单例对象的场景。
多线程下安全的单例模式
class Singleton
{
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* GetInstance()
{
std::unique_lock<std::mutex> lock(mutex_);
if (nullptr == instance_)//
{
instance_ = new Singleton();
}
return instance_;
}
private:
class CGarbo
{
public:
~CGarbo()
{
if (nullptr != Singleton::instance_)
{
delete Singleton::instance_;
}
}
};
static CGarbo cGarbo_; //创建这个静态对象,程序结束后系统会调用它的析构函数从而会释放单例对象
static Singleton* instance_;
static std::mutex mutex_;
};
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;
Singleton::CGarbo Singleton::cGarbo_;
对于这个代码,无论当单例实例有没有被创建,我们都会加锁,实际上,在多线程中,只有当实例没有被创建的时候,我们多个线程同时创建时才会出现问题,而当实例对象已经创建了,此时我们使用这个单例对象是安全的。
而此时,当单例对象存在时,我们使用时也需要争夺锁,效率太低。
下面为优化版本的。
class Singleton
{
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* GetInstance()
{
if (nullptr == instance_)
{
std::unique_lock<std::mutex> lock(mutex_);
}
if (nullptr == instance_)
{
instance_ = new Singleton();
}
return instance_;
}
private:
class CGarbo
{
public:
~CGarbo()
{
if (nullptr != Singleton::instance_)
{
delete Singleton::instance_;
}
}
};
static CGarbo cGarbo_; //创建这个静态对象,程序结束后系统会调用它的析构函数从而会释放单例对象
static Singleton* instance_;
static std::mutex mutex_;
};
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;
Singleton::CGarbo Singleton::cGarbo_;
第一个if的作用:由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用,就不用竞争锁。如果已经创建了实例的话,直接返回前面创建的实例即可。
第二个if的作用:假如A,B线程同时运行到第一个判空操作,此时都为空,接着A抢到锁之后创建了单例对象,如果没有第二个if的话,等到B抢到锁,此时它按照之前执行的地方执行,又会创建一次单例对象。而此时有了if判断,则会避免这种情况。