什么是单例模式?
单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。
单例模式分类
单例模式可以分为懒汉式和饿汉式,两者之间的区别在于创建实例的时间不同:
- 懒汉式:指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
- 饿汉式:指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)
单例类特点
- 构造函数和析构函数为private类型,目的禁止外部构造和析构
- 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
- 类里有个获取实例的静态函数,可以全局访问
懒汉单例(线程不安全)
#include<iostream>
class singleton {
private:
singleton() {}
static singleton* p;
public:
static singleton* instance() {
if (p == nullptr)
p = new singleton;
return p;
}
};
singleton* singleton::p = nullptr;
int main() {
auto a = singleton::instance();
delete a;
}
饿汉单例
#include<iostream>
class singleton {
private:
singleton() {}
static singleton* p;
public:
static singleton* instance() {
return p;
}
};
singleton* singleton::p = new singleton();
int main() {
auto a = singleton::instance();
delete a;
}
静态成员的初始化器可以访问类的私有与保护成员,所以这里可以使用new定义并初始化静态成员
线程安全饿汉单例
#include<thread>
#include<iostream>
#include<mutex>
class SingleInstance {
public:
static SingleInstance*& GetInstance();
static void deleteInstance();
void Print() {
std::cout << this << '\n';
}
private:
SingleInstance() {
std::cout << __func__ << '\n';
}
~SingleInstance() {
std::cout << __func__ << '\n';
}
SingleInstance(const SingleInstance& signal);
const SingleInstance& operator=(const SingleInstance& signal) = delete;
inline static SingleInstance* m_SingleInstance = nullptr;
inline static std::mutex m_Mutex;
};
SingleInstance*& SingleInstance::GetInstance() {
if (m_SingleInstance == nullptr) {
std::lock_guard<std::mutex> lock(m_Mutex);
if (m_SingleInstance == nullptr) {
m_SingleInstance = new (std::nothrow) SingleInstance{};
}
}
return m_SingleInstance;
}
void SingleInstance::deleteInstance() {
std::lock_guard<std::mutex> lock(m_Mutex);
if (m_SingleInstance) {
delete m_SingleInstance;
m_SingleInstance = nullptr;
}
}
int main() {
decltype(auto) p = SingleInstance::GetInstance();
p->Print();
SingleInstance::deleteInstance();
decltype(auto) p2 = SingleInstance::GetInstance();
p2->Print();
SingleInstance::deleteInstance();
decltype(auto) p3 = SingleInstance::GetInstance();
p3->Print();
SingleInstance::deleteInstance();
}
注意这至少要求c++17标准,使用了inline修饰变量,可以看下面
inline 说明符 - cppreference.comhttps://zh.cppreference.com/w/cpp/language/inline
静态数据成员
静态数据成员不关联到任何对象。即使不定义类的任何对象它们也存在。整个程序中只有一个拥有静态存储期的静态数据成员实例,除非使用关键词 thread_local,此时每个线程都有一个具有线程存储期的该对象 (C++11 起)。
静态数据成员不能是 mutable 的。
在命名空间作用域中,如果类自身具有外部连接(即不是无名命名空间的成员),那么类的静态数据成员也具有外部连接。局部类(定义于函数内部的类)和无名类,包括无名类的成员类,不能拥有静态数据成员。
静态数据成员可以声明为 inline。 inline 静态数据成员可以在类定义中定义,而且可以指定初始化器。它不需要类外定义: | (C++17 起) |
静态成员 - cppreference.comhttps://zh.cppreference.com/w/cpp/language/static