IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++ 正确的单例模式 -> 正文阅读

[C++知识库]c++ 正确的单例模式

1.单例模式

单例模式是最最常见的一种模式,平时开发过程中用的也挺多的,目的是:
只有一个实例,这个实例可以只持有一份全局共用的数据,也可以是仅持有一堆接口,等等,能用上单例模式的很多。

最近在检视别人和自己的代码过程中,发现了一些常见的错误/问题,在此处列举一下

2.单例模式规范

2.1 单例模式的接口类型:

1.返回引用型

Singleton &Singleton::GetInstance()
{
    static Singleton instance;
    return instance;
}

2.返回指针型

Singleton *Singleton::GetInstance()
{
    static Singleton instance;
    return &instance;
}

常见错误:
对于c++初学者/刚切c++语言的使用者来说,可能引用/指针的概念不太清晰,可能写成了:

Singleton Singleton::GetInstance()
{
    static Singleton instance;
    return instance;
}

这样子是达不到单例效果的,每次都会产生一拷贝构造

个人习惯用引用的方式,后面就以引用为例子

2.2 单例模式的构造函数必须显式声明为非public(最好是private)

众所周知。如果构造函数是public的话,就达不到单例的目的了
常见错误: 忘记显式声明(一般没有傻到显式声明构造函数还用单例的吧)

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
};

因为没有声明,所以别人可以在外部创建

2.3 单例模式必须禁用拷贝构造、拷贝赋值、移动构造、移动赋值

曾经检视过一个同事的代码,单例写成了

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // 构造函数显式声明为private
};

void Func() {
    auto instance = Singleton::GetInstance();
}

注:
auto不会自动推导出引用
所以这里做的事情等价于:

auto &instanceRef = Singleton::GetInstance();
auto instance = instanceRef;

这里产生了一次拷贝构造

如何从代码的角度完全避免这种行为呢?
就需要将所有的拷贝、移动相关的都显式禁止

正确做法如下:

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // 构造函数显式声明为private
    DISABLE_COPY_AND_MOVE(Singleton);
};

void Func() {
    auto instance = Singleton::GetInstance();
}

DISABLE_COPY_AND_MOVE 将上述四个都显式删除了
这时候会发现
auto instance = Singleton::GetInstance();
编译不过

通过这种方式可以保证单例不会发生任何的拷贝或移动
即别人写出
std::move / auto instance = …之类的行为,都是不被允许的

2.3 单例类的析构必须显式声明为private

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // 构造函数显式声明为private
    DISABLE_COPY_AND_MOVE(Singleton);
};

void Func() {
    auto &instance = Singleton::GetInstance();
    auto ptr = &instance;
    delete ptr;
}

假如代码使用场景中,通过某些方式持有到了单例的指针,(尤其是unique_ptr),那么这个指针在析构时,就会将指针释放

如上面的写法,很恶意的delete了这个指针,也是可以的。
删除指针后,别人拿到的单例的引用/指针,就会导致程序崩溃

正确做法:

#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \
    _CLASS_NAME(const _CLASS_NAME&) = delete; \
    _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \
    _CLASS_NAME(_CLASS_NAME &&) = delete; \
    _CLASS_NAME& operator=(_CLASS_NAME &&) = delete;

class Singleton {
public:
	static Singleton &GetInstance()
	{
	    static Singleton instance;
	    return instance;
	}
private:
    Singleton() = default; // 构造函数显式声明为private
    ~Singleton() = default; // 析构函数显式声明为private
    DISABLE_COPY_AND_MOVE(Singleton); // 禁止移动、拷贝
};
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:14:26  更:2022-03-12 17:16:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:33:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码