C++设计模式:单例模式 (现代C++主流实现方式Meyer‘s Singleton + 使用CRTP模板化)

文章目录

  • 单例模式
  • 创建单例类
  • 饿汉式or懒汉式
  • [现代C++单例模式的主流实现方式------Meyer's Singleton](#现代C++单例模式的主流实现方式——Meyer's Singleton)
  • [使用 CRTP 模板化单例类](#使用 CRTP 模板化单例类)

单例模式

单例模式是指程序中只需要一个实例化对象,在全局作用域或整个代码架构中,此对象只被实例化一次,就可以达到在整个程序生命周期中被使用的目的。假如程序中设计了单例模式类,但是在程序设计中实例化了多个对象,那么这些对象也只占用同一块地址空间,在代码中可以通过"%p"输出的内存地址看出,这些对象是唯一的实例。

创建单例类

如何保证类的实例有且仅有一个?

涉及一个类多对象操作的函数有以下几个:

  • 构造函数:创建一个新的对象
  • 拷贝构造函数:根据已有对象拷贝出一个新的对象
  • 拷贝赋值操作符重载函数:两个对象之间的赋值

为了把一个类可以实例化多个对象的路堵死,可以做如下处理:

  • 构造函数私有化,在类内部只调用一次,这个是可控的。
    • 由于使用者在类外部不能使用构造函数,所以在类内部创建的这个唯一的对象必须是静态的,这样就可以通过类名来访问了,为了不破坏类的封装,我们都会把这个静态对象的访问权限设置为私有的。
      在类中只有它的静态成员函数才能访问其静态成员变量,所以可以给这个单例类提供一个静态函数用于得到这个静态的单例对象。
  • 拷贝构造函数私有化或者禁用(使用 = delete)
  • 拷贝赋值操作符重载函数私有化或者禁用(从单例的语义上讲这个函数已经毫无意义,所以在类中不再提供这样一个函数,故将它也一并处理一下。)
cpp 复制代码
// 定义一个单例模式的类
class Singleton
{
public:
    Singleton(const Singleton& obj) = delete;
    Singleton& operator=(const Singleton& obj) = delete;
    static Singleton* GetInstance()
    {
        return instance;
    }
private:
    Singleton() = default;
    static Singleton* instance;
};

//静态成员变量只能在类外部初始化
Singleton* Singleton::instance = new Singleton();

饿汉式or懒汉式

饿汉模式 就是在类加载的时候立刻进行实例化,这样就得到了一个唯一的可用对象。

适用于内存大的场景,多线程调用没有线程安全问题。

懒汉模式 是在类加载的时候不去创建这个唯一的实例,而是在需要使用的时候再进行实例化。

适用于内存紧张的场景,多线程调用有线程安全问题。

参考文章:https://subingwen.cn/design-patterns/singleton/

现代C++单例模式的主流实现方式------Meyer's Singleton

Meyer's Singleton是典型的懒汉模式:

  • 线程安全(C++11 起):局部静态变量的初始化是线程安全的(编译器会自动加锁)。
  • 延迟初始化(Lazy Initialization):只有当 GetInstance() 第一次被调用时才会构造对象。
  • 自动资源管理:程序结束时,静态变量会自动析构(除非你使用了动态分配)。
  • 高效无拷贝:返回的是引用,不会产生拷贝,效率高。
cpp 复制代码
class Singleton
{
public:
    Singleton(const Singleton& obj) = delete;
    Singleton& operator=(const Singleton& obj) = delete;
    static Singleton& GetInstance()
    {
    	static Singleton instance;
        return instance;
    }
private:
    Singleton() = default;
    ~Singleton() = default;
};

使用 CRTP 模板化单例类

CRTP(Curiously Recurring Template Pattern)奇异递归模板模式 是C++中的一个编程技巧,它允许基类使用派生类的类型信息。这种模式在静态多态、计数器等场景中有应用,例如在LLVM项目中被广泛使用。通过CRTP,可以避免虚函数调用的开销,提供更高效和灵活的代码设计。
CRTP的特性表现为:

  • 基类是一个模板类
  • 派生类继承该基类时,将派生类自身作为模板参数传递给基类

实现一个 模板化的单例类,并使用 CRTP来让任意类轻松成为单例,可以这样写:

cpp 复制代码
template<typename T>
class Singleton
{
protected:
    Singleton() = default;
    ~Singleton() = default;

public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    static T& GetInstance()
    {
        static T instance;
        return instance;
    }
};

template<typename T> T& g = Singleton<T>::GetInstance();

使用方式

cpp 复制代码
class MyClass : public Singleton<MyClass>
{
    friend class Singleton<MyClass>; // 保证 GetInstance 可以访问构造函数
private:
    MyClass() { /* 构造逻辑 */ }
public:
    void DoSomething() { /* ... */ }
};

// 使用
MyClass::GetInstance().DoSomething();
// 或
g<MyClass>.DoSomething();
相关推荐
cccyi78 分钟前
c++-string
c++·string
云边有个稻草人2 小时前
【C++】第十八节—一文万字详解 | map和set的使用
c++·set·map·multimap·multiset·序列式容器和关联式容器
玩代码3 小时前
组合设计模式
java·设计模式
命运之光3 小时前
【最新】Java的几种设计模式详解及适用业务场景
java·开发语言·设计模式
wwww.wwww3 小时前
使用qt编写上位机程序,出现串口死掉无法接受数据的bug
c++·qt·bug
大葱明耗子4 小时前
拓展三字棋
数据结构·c++·算法
卜及中5 小时前
【C++11】哈希表与无序容器:从概念到应用
c++·算法·哈希算法
dowhileprogramming6 小时前
C++ 中重载函数右值引用和左值引用匹配的优先级
java·开发语言·c++
西哥写代码6 小时前
vs2017 c++ 使用sqlite3数据库
c++·sqlite·vs2017
Littlewith6 小时前
Node.js:Web模块、Express框架
java·开发语言·前端·c++·后端·node.js·express