C++设计模式

一、单例模式(Singleton Pattern)

1. 设计本质与动机

单例的核心诉求是全局唯一 + 统一访问入口,本质是用类的封装性替代全局变量,同时对实例化时机、生命周期、创建逻辑进行可控管理。它要解决的核心问题是:避免一个全局资源被重复创建、多实例状态不一致,同时屏蔽对象创建细节。

2. 核心角色与结构

  • 单例类:私有化构造 / 拷贝 / 赋值,内部持有唯一实例,对外提供静态获取接口。
  • 无其他外部角色,调用方直接通过类名访问。

3. 主流实现方案与 C++ 细节

方案 1:饿汉式(程序启动即创建)

实例在程序进入 main 函数前就完成初始化,天生线程安全。

cpp 复制代码
class HungrySingleton {
private:
    HungrySingleton() = default;
    HungrySingleton(const HungrySingleton&) = delete;
    HungrySingleton& operator=(const HungrySingleton&) = delete;
    ~HungrySingleton() = default;

    static HungrySingleton instance; // 静态成员,程序启动即构造
public:
    static HungrySingleton& getInstance() {
        return instance;
    }
};
// 类外初始化
HungrySingleton HungrySingleton::instance;
  • 优点:无锁、线程安全、实现简单。
  • 缺点:启动即占用资源;若单例依赖外部初始化参数则无法使用;存在 "静态初始化顺序问题"(不同编译单元的静态变量初始化顺序不确定)。
方案 2:懒汉式

第一次使用时才创建,通过双重检查减少锁的开销

cpp 复制代码
#include <mutex>
#include <memory>

class LazySingleton {
private:
    LazySingleton() = default;
    LazySingleton(const LazySingleton&) = delete;
    LazySingleton& operator=(const LazySingleton&) = delete;

    static std::unique_ptr<LazySingleton> instance;
    static std::mutex mtx;
public:
    static LazySingleton* getInstance() {
        if (instance == nullptr) {         // 第一次检查:无锁快速判断
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {     // 第二次检查:加锁后确认
                instance.reset(new LazySingleton());
            }
        }
        return instance.get();
    }
};

2. 简单工厂模式(Simple Factory)

核心定义

一个工厂类根据传入的参数,动态决定创建哪一种产品的实例。又称静态工厂模式,不属于 GoF 23 种设计模式,是工厂模式的基础形态。

适用场景

  • 产品种类较少、创建逻辑简单
  • 调用方不关心对象创建细节,只需要获取对象
  • 需求稳定,不会频繁新增产品类型

C++ 代码实现

以图形创建为例:

cpp 复制代码
#include <iostream>
#include <string>

// 抽象产品
class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

// 具体产品:圆形
class Circle : public Shape {
public:
    void draw() override { std::cout << "绘制圆形" << std::endl; }
};

// 具体产品:矩形
class Rectangle : public Shape {
public:
    void draw() override { std::cout << "绘制矩形" << std::endl; }
};

// 工厂类:统一创建所有产品
class ShapeFactory {
public:
    static Shape* createShape(const std::string& type) {
        if (type == "circle")      return new Circle();
        else if (type == "rect")   return new Rectangle();
        return nullptr;
    }
};

// 使用方式
int main() {
    Shape* s1 = ShapeFactory::createShape("circle");
    s1->draw();
    Shape* s2 = ShapeFactory::createShape("rect");
    s2->draw();
    delete s1; delete s2;
    return 0;
}

优缺点

  • 优点:实现极简,对象创建与使用分离,调用方无需感知具体产品类。
  • 缺点:违反开闭原则,新增产品必须修改工厂类代码;产品多时工厂类职责臃肿。

3. 工厂方法模式(Factory Method)

核心定义

定义一个创建产品的抽象工厂接口,让具体工厂子类决定实例化哪一个具体产品。将产品的创建延迟到工厂子类中完成,是 GoF 标准创建型模式。

核心区别(对比简单工厂)

每个具体产品对应一个专属的具体工厂,新增产品时只需新增产品类 + 工厂类,无需修改原有代码。

适用场景

  • 系统无法预知需要创建的对象的具体类型
  • 希望由子类决定创建哪个具体对象
  • 需要灵活扩展产品种类,且不影响原有代码

C++ 代码实现

cpp 复制代码
#include <iostream>

// 抽象产品
class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

// 具体产品
class Circle : public Shape {
public:
    void draw() override { std::cout << "绘制圆形" << std::endl; }
};
class Rectangle : public Shape {
public:
    void draw() override { std::cout << "绘制矩形" << std::endl; }
};

// 抽象工厂
class ShapeFactory {
public:
    virtual Shape* createShape() = 0;
    virtual ~ShapeFactory() = default;
};

// 具体工厂:每个产品对应一个工厂
class CircleFactory : public ShapeFactory {
public:
    Shape* createShape() override { return new Circle(); }
};
class RectangleFactory : public ShapeFactory {
public:
    Shape* createShape() override { return new Rectangle(); }
};

// 使用方式
int main() {
    ShapeFactory* factory = new CircleFactory();
    Shape* shape = factory->createShape();
    shape->draw();
    delete factory; delete shape;
    return 0;
}

优缺点

  • 优点:完全符合开闭原则,新增产品只需新增对应工厂类;高层模块只依赖抽象,解耦彻底。
  • 缺点:类数量成对增长,产品多时会出现类爆炸,提升系统复杂度。

4. 抽象工厂模式(Abstract Factory)

核心定义

提供一个创建一系列相关 / 相互依赖对象的接口,无需指定它们的具体类。是 GoF 标准创建型模式,针对「产品族」而非单个产品。

核心概念

  • 产品等级:同一种产品的不同实现(如按钮分 Windows 按钮、Mac 按钮)
  • 产品族:同一个工厂生产的、配套使用的一组产品(如 Windows 工厂生产 Windows 按钮 + Windows 文本框)

工厂方法面向单个产品等级 ,抽象工厂面向多个产品等级(产品族)

适用场景

  • 系统需要多套产品族,且同产品族的产品必须配套使用
  • 需要一键切换整套产品(如一键切换 UI 主题)
  • 产品等级结构稳定,不会频繁新增产品类型

C++ 代码实现

以跨平台 UI 组件为例(按钮 + 文本框两个产品等级,Windows+Mac 两个产品族):

cpp 复制代码
#include <iostream>

// 抽象产品A:按钮
class Button {
public:
    virtual void click() = 0;
    virtual ~Button() = default;
};
// 具体产品A
class WindowsButton : public Button {
public:
    void click() override { std::cout << "Windows按钮点击" << std::endl; }
};
class MacButton : public Button {
public:
    void click() override { std::cout << "Mac按钮点击" << std::endl; }
};

// 抽象产品B:文本框
class TextBox {
public:
    virtual void input() = 0;
    virtual ~TextBox() = default;
};
// 具体产品B
class WindowsTextBox : public TextBox {
public:
    void input() override { std::cout << "Windows文本框输入" << std::endl; }
};
class MacTextBox : public TextBox {
public:
    void input() override { std::cout << "Mac文本框输入" << std::endl; }
};

// 抽象工厂:定义创建所有产品等级的接口
class UIFactory {
public:
    virtual Button* createButton() = 0;
    virtual TextBox* createTextBox() = 0;
    virtual ~UIFactory() = default;
};

// 具体工厂:Windows产品族工厂
class WindowsUIFactory : public UIFactory {
public:
    Button* createButton() override { return new WindowsButton(); }
    TextBox* createTextBox() override { return new WindowsTextBox(); }
};
// 具体工厂:Mac产品族工厂
class MacUIFactory : public UIFactory {
public:
    Button* createButton() override { return new MacButton(); }
    TextBox* createTextBox() override { return new MacTextBox(); }
};

// 使用方式:更换工厂即可切换整套UI风格
int main() {
    UIFactory* factory = new WindowsUIFactory();
    Button* btn = factory->createButton();
    TextBox* box = factory->createTextBox();
    btn->click();
    box->input();
    delete factory; delete btn; delete box;
    return 0;
}

优缺点

  • 优点:隔离具体类生成,客户端无需感知产品细节;产品族切换成本极低;保证同产品族产品配套使用。
  • 缺点:开闭原则倾斜------ 新增产品族容易(加新工厂),新增产品等级极难(需要修改所有工厂类)。

5. 观察者模式(Observer Pattern)

核心定义

定义对象间一对多的依赖关系:当一个对象(主题 / 被观察者)的状态发生改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。又称发布 - 订阅模式,是 GoF 标准行为型模式。

核心思想

主题维护一个观察者列表,提供注册、移除、通知三个接口;观察者实现统一的更新接口。状态变化时,主题遍历列表,逐个通知所有观察者。

适用场景

  • 一个对象的状态变化需要联动通知多个其他对象
  • 对象间需要松耦合的联动,被观察者无需知道观察者的业务细节
  • 事件订阅、消息推送、UI 数据绑定、MVC 架构等场景

C++ 代码实现

以公众号订阅为例:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>

// 抽象观察者
class Observer {
public:
    virtual void update(const std::string& msg) = 0;
    virtual ~Observer() = default;
};

// 抽象主题(被观察者)
class Subject {
public:
    virtual void attach(Observer* obs) = 0;  // 订阅
    virtual void detach(Observer* obs) = 0;  // 取消订阅
    virtual void notify() = 0;               // 广播通知
    virtual ~Subject() = default;
};

// 具体主题:公众号
class OfficialAccount : public Subject {
private:
    std::vector<Observer*> observers;
    std::string article;
public:
    void attach(Observer* obs) override { observers.push_back(obs); }

    void detach(Observer* obs) override {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == obs) { observers.erase(it); break; }
        }
    }

    void notify() override {
        for (auto obs : observers) obs->update(article);
    }

    void publish(const std::string& title) {
        article = title;
        std::cout << "公众号发布:" << title << std::endl;
        notify();
    }
};

// 具体观察者:用户
class User : public Observer {
private:
    std::string name;
public:
    User(std::string n) : name(std::move(n)) {}
    void update(const std::string& msg) override {
        std::cout << "用户【" << name << "】收到推送:" << msg << std::endl;
    }
};

// 使用方式
int main() {
    OfficialAccount account;
    User u1("张三"), u2("李四");

    account.attach(&u1);
    account.attach(&u2);
    account.publish("C++设计模式入门");

    account.detach(&u1);
    account.publish("观察者模式实战");
    return 0;
}

优缺点

  • 优点:主题与观察者松耦合,可独立扩展;支持广播通信,一次触发全员通知。
  • 缺点:观察者数量多时通知效率低;循环依赖会触发死循环;主题只通知状态变化,不传递变化细节。
相关推荐
h_a_o777oah2 小时前
【算法专项】扩展域并查集:原理详解及解决大部分种类并查集问题(洛谷P5937 P2024 C++代码)
数据结构·c++·算法·acm·并查集·扩展域·逻辑建模
智码看视界3 小时前
老梁聊全栈:JavaScript 原型链深入探索对象继承的奥秘
前端·javascript·ecmascript
智码看视界3 小时前
老梁聊全栈系列 JavaScript语言本质:从原型链到异步编程的深度解析
开发语言·javascript·全栈·javascript核心
雾沉川3 小时前
Visual C++ 运行库合集 v105.0 部署与故障排查技术指南
开发语言·c++·dll
丘山望岳3 小时前
剑起霜华——平衡二叉树(AVL树 )精讲
开发语言·数据结构·c++
Boom_Shu4 小时前
浅拷贝与深拷贝
开发语言·c++·算法
Mortalbreeze4 小时前
C++ Lambda表达式详解:从捕获列表到底层原理
开发语言·c++
为何创造硅基生物4 小时前
LVGL
c++·ui
只做人间不老仙4 小时前
C++ grpc 拦截器示例学习
开发语言·c++·学习