C++传记 详解单例模式(面向对象)

核心说明:单例模式是C++面向对象编程中最基础、最常用的设计模式之一,属于"创建型模式"。其核心目标是 确保一个类在整个程序生命周期中,只存在一个实例对象,并提供一个全局唯一的访问入口,避免重复创建对象造成的资源浪费、数据不一致等问题。本笔记从基础概念、实现约束、常见版本、注意事项到实战场景,逐步拆解,适合C++初学者夯实面向对象基础,也可作为复习巩固的参考。

一、核心概念(必记)

  1. 定义:单例模式(Singleton Pattern)是一种面向对象设计模式,用于保证一个类仅有一个实例,并提供一个访问该实例的全局访问点,不允许外部随意创建该类的对象。

  2. 核心需求:解决"全局唯一对象"的访问问题,常见应用场景:日志器、配置管理器、数据库连接池、全局计数器等(这类对象只需一个,多实例会导致资源浪费或数据错乱)。

  3. 设计本质:通过C++的访问控制(private/public)和静态成员特性,垄断类的实例创建权,禁止外部实例化、拷贝,仅暴露唯一访问接口。

二、单例模式的实现约束(重中之重)

要实现一个标准的单例类,必须满足3个核心约束(缺一不可,否则会出现"伪单例"),这是单例模式的基础,必须牢记:

  1. 私有构造函数(含默认构造、带参构造):将构造函数设为private,禁止外部通过 new 关键字创建实例,从根源上杜绝外部实例化。

  2. 私有拷贝构造函数和赋值运算符:禁止外部通过拷贝(Singleton s = Singleton::getInstance())、赋值(s1 = s2)的方式创建新实例,避免出现多个实例。C++11及以上可直接用 = delete 禁用。

  3. 公有静态访问接口:提供一个静态成员函数(通常命名为 getInstance()),作为外部访问单例实例的唯一入口,该函数负责创建并返回单例实例。

三、常见实现版本(分场景使用,必练)

单例模式的实现主要分为两大类型:饿汉式(提前创建)和懒汉式(延迟创建),不同版本各有优劣,需根据实际场景选择,以下是C++中最常用的4个版本,附完整可运行代码和核心解析。

版本1:饿汉式单例(基础版,线程安全)

核心思想

程序启动时(全局静态变量初始化阶段),就主动创建单例实例,后续调用访问接口时,直接返回已创建的实例,无需动态创建。

完整代码
cpp 复制代码
#include <iostream>
using namespace std;

// 饿汉式单例类
class Singleton {
private:
    // 1. 私有构造函数(禁止外部实例化)
    Singleton() {
        cout << "饿汉式单例实例创建" << endl;
    }
    // 2. 私有拷贝构造和赋值运算符(禁止拷贝)
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 3. 私有静态成员(程序启动时初始化,全局唯一)
    static Singleton instance;

public:
    // 4. 公有静态访问接口(唯一入口)
    static Singleton& getInstance() {
        return instance;
    }
    
    // 测试方法(模拟实际功能)
    void show() {
        cout << "单例实例正在工作(饿汉式)" << endl;
    }
};

// 关键:全局区初始化静态实例(程序启动时执行,仅一次)
Singleton Singleton::instance;

// 测试代码(可直接运行)
int main() {
    // 只能通过getInstance()访问实例,无法new、无法拷贝
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    
    // 验证实例唯一性(地址相同即为同一实例)
    cout << "s1地址:" << &s1 << endl;
    cout << "s2地址:" << &s2 << endl;
    
    s1.show();
    return 0;
}
    
优缺点及适用场景

优点:实现简单,无需考虑线程安全(C++中全局静态变量的初始化是线程安全的),访问速度快,无延迟。

缺点:实例提前创建,若实例创建成本高(如加载大量配置、创建数据库连接),且程序全程未使用,会造成资源浪费。

适用场景:单例实例创建成本低、程序启动后一定会使用该实例(如简单的全局配置管理器)。

版本2:懒汉式单例(基础版,非线程安全)

核心思想

延迟加载:程序启动时不创建实例,第一次调用 getInstance() 时,才创建实例,后续调用直接返回,避免资源浪费。

完整代码
cpp 复制代码
#include <iostream>
using namespace std;

class Singleton {
private:
    Singleton() {
        cout << "懒汉式单例实例创建" << endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 静态指针,初始化为nullptr(不提前创建实例)
    static Singleton* instance;

public:
    static Singleton* getInstance() {
        // 第一次调用时创建实例
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
    
    void show() {
        cout << "单例实例正在工作(懒汉式基础版)" << endl;
    }
    
    // 可选:手动释放内存(避免内存泄漏)
    static void destroyInstance() {
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }
};

// 初始化静态指针为nullptr
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    
    cout << "s1地址:" << s1 << endl;
    cout << "s2地址:" << s2 << endl;
    
    s1->show();
    Singleton::destroyInstance(); // 手动释放
    return 0;
}
    
优缺点及适用场景

优点:延迟加载,避免资源浪费,实例仅在需要时创建。

缺点:非线程安全!多线程环境下,多个线程可能同时进入 if (instance == nullptr),导致创建多个实例,打破单例约束。

适用场景:仅适用于单线程环境(如简单的控制台程序),实际开发中(多线程)禁止使用。

版本3:懒汉式单例(线程安全版,推荐)

核心思想

在基础懒汉式的基础上,添加互斥锁(std::mutex),保证多线程环境下,只有一个线程能创建实例;同时采用"双重检查锁定(DCLP)",减少锁的开销,兼顾线程安全和效率。

完整代码
cpp 复制代码
#include <iostream>
#include <mutex> // 需包含互斥锁头文件
using namespace std;

class Singleton {
private:
    Singleton() {
        cout << "线程安全版懒汉式单例实例创建" << endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    static Singleton* instance;
    static mutex mtx; // 互斥锁,保证线程安全

public:
    static Singleton* getInstance() {
        // 双重检查锁定(DCLP):减少锁的开销
        if (instance == nullptr) { // 第一次检查:避免每次调用都加锁
            lock_guard<mutex> lock(mtx); // 加锁,确保同一时间只有一个线程进入
            if (instance == nullptr) { // 第二次检查:防止多个线程同时通过第一次检查
                instance = new Singleton();
            }
        }
        return instance;
    }
    
    void show() {
        cout << "单例实例正在工作(线程安全版)" << endl;
    }
    
    static void destroyInstance() {
        lock_guard<mutex> lock(mtx); // 释放时也加锁,避免线程安全问题
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
mutex Singleton::mtx;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    
    cout << "s1地址:" << s1 << endl;
    cout << "s2地址:" << s2 << endl;
    
    s1->show();
    Singleton::destroyInstance();
    return 0;
}
    
关键注意点
  1. 互斥锁(std::mutex):C++11及以上标准才支持,需包含 <mutex> 头文件。

  2. 双重检查锁定(DCLP):两次检查 instance == nullptr,第一次检查避免频繁加锁(提升效率),第二次检查确保唯一实例。

  3. 内存释放:手动调用 destroyInstance() 释放内存,避免内存泄漏;也可结合智能指针(std::unique_ptr)自动管理内存(后续优化版会讲)。

适用场景:多线程环境(如服务器程序、多线程控制台程序),是实际开发中最常用的懒汉式版本。

版本4:现代C++懒汉式(局部静态变量版,最优)

核心思想

利用C++11的特性:局部静态变量的初始化是线程安全的------多个线程同时访问局部静态变量的初始化语句时,只会有一个线程执行初始化,其他线程阻塞等待。无需手动加锁,代码更简洁、安全。

完整代码
cpp 复制代码
#include <iostream>
using namespace std;

class Singleton {
private:
    Singleton() {
        cout << "现代C++懒汉式单例实例创建" << endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    // 局部静态变量实现:第一次调用时初始化,仅初始化一次
    static Singleton& getInstance() {
        static Singleton instance; // 局部静态变量,线程安全(C++11及以上)
        return instance;
    }
    
    void show() {
        cout << "单例实例正在工作(现代C++版)" << endl;
    }
};

int main() {
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    
    cout << "s1地址:" << &s1 << endl;
    cout << "s2地址:" << &s2 << endl;
    
    s1.show();
    return 0;
}
    
优缺点及适用场景

优点:代码最简洁(无需管理静态指针、互斥锁),线程安全(依赖C++11特性),延迟加载,无内存泄漏(局部静态变量在程序结束时自动销毁)。

缺点:仅支持C++11及以上标准,若项目需兼容旧标准(如C++98),则无法使用。

适用场景:C++11及以上环境,是现代C++开发中最推荐的单例实现方式(简洁、安全、高效)。

四、常见问题与注意事项(易错点,必记)

  1. 避免"伪单例":必须同时禁用构造函数、拷贝构造、赋值运算符,否则外部可能通过拷贝、赋值创建新实例。

  2. 线程安全问题:仅饿汉式(全局静态变量)、线程安全懒汉式(加锁)、现代C++懒汉式(局部静态变量)支持多线程,基础懒汉式不可用于多线程。

  3. 内存泄漏:懒汉式(指针版)需手动释放内存(如调用 destroyInstance()),或使用智能指针优化;局部静态变量版无需手动释放。

  4. 静态成员初始化:饿汉式的静态实例需在类外初始化,否则会编译报错。

  5. 不能滥用单例:单例模式会增加类的耦合度,若一个类不需要全局唯一实例,切勿强行使用单例(如普通的工具类)。

五、实战应用场景(结合C++面向对象)

单例模式的核心价值是"全局唯一",以下是C++开发中最常见的实战场景,可直接参考使用:

  1. 日志器(Logger):整个程序只需一个日志器实例,负责所有模块的日志输出,避免多个日志器同时写入导致日志错乱。

  2. 配置管理器(ConfigManager):读取配置文件(如config.ini),全局提供配置参数(如端口号、数据库地址),避免重复读取配置文件。

  3. 数据库连接池(DBConnectionPool):管理数据库连接,避免频繁创建、销毁连接造成的性能损耗,确保连接资源唯一。

  4. 全局计数器:统计程序中某个事件的发生次数(如接口调用次数),全局唯一计数,避免多实例计数混乱。

六、总结(核心要点提炼)

  1. 单例模式核心:唯一实例 + 全局唯一访问入口。

  2. 实现约束:私有构造、私有拷贝/赋值、公有静态访问接口。

  3. 常用版本选择:

  • 简单场景、C++11以下:饿汉式。

  • 多线程、C++11以下:线程安全懒汉式(加锁+DCLP)。

  • 多线程、C++11及以上:现代C++懒汉式(局部静态变量,最优)。

  1. 注意:不滥用、防伪单例、防线程安全问题、防内存泄漏。
相关推荐
扶摇接北海1762 小时前
洛谷:B4488 [语言月赛 202602] 甜品食用
数据结构·c++·算法
cui_ruicheng2 小时前
C++智能指针:从 RAII 到 shared_ptr 源码实现
开发语言·c++
爱丽_2 小时前
AQS 的 CLH 同步队列:入队/出队、park/unpark 与“公平性”从哪来
java·开发语言·jvm
共享家95272 小时前
实现简化的高性能并发内存池
开发语言·数据结构·c++·后端
千里马学框架2 小时前
aospc/c++的native 模块VScode和Clion
android·开发语言·c++·vscode·安卓framework开发·clion·车载开发
liuqun03192 小时前
go进阶之gc
开发语言·后端·golang
武藤一雄2 小时前
深入理解 C# 中的 sizeof 与非托管类型约束
开发语言·windows·c#·.net·.netcore
好家伙VCC2 小时前
**发散创新:用 Rust实现数据编织(DataWrangling)的高效流式处理架构**在现
java·开发语言·python·架构·rust
2401_876907522 小时前
《Python深度学习》
开发语言·python·深度学习