【QT C++】常用设计模式

前言

本篇的设计模式主要是:单例模式、工厂方法模式、策略模式、观察者模式、适配器模式。

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

特点

  • 唯一实例:类自己控制实例创建,保证全局只有一个对象。
  • 私有构造函数:构造函数设为private ,不适合被继承,且外部不能随意 new 对象。
  • 禁止拷贝和赋值:删除拷贝构造、拷贝赋值、移动构造、移动赋值,防止通过复制产生第二个实例。
  • 提供全局访问点:通过静态方法如**instance() 或 getInstance()**返回唯一实例的引用或指针。
  • 内部只创建一次对象:通常用**static 局部变量**实现。

样例及说明

单例模式因为构造函数设为private ,不适合被继承,所以它的模式中很少有virtual虚函数(C++语法上是可以有,因为不适合被继承,所以很少有虚函数)。static 局部变量 使得它的生命周期从第一次执行到该声明语句时 进行构造和初始化,而结束则是析构发生在 main() 函数返回之后。

static 局部变量的特性在【C++开发】从裸指针到智能指针-CSDN博客中有介绍。

单例模式类实现

复制代码
//configmanager.h
#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H

#include <QObject>
#include <QSettings>

/******************************************************
 * 配置管理类(单例模式)
 * 用于统一管理应用程序的配置信息,例如用户名、窗口大小等。
 * 使用 QSettings 进行本地持久化存储。
 * 该类禁止拷贝和赋值,确保全局只有一个实例。
 ******************************************************/
class ConfigManager : public QObject
{
    Q_OBJECT

public:    //外部可调用的类成员函数
    //获取 ConfigManager 全局唯一实例
    //采用局部静态变量实现线程安全
    static ConfigManager &instance();

    QString userName() const;              //返回配置文件中的用户名称
    void setUserName(const QString &name); //修改配置文件中的用户名称

    //保存当前配置到持久化存储
    //通常在程序退出或配置变更时调用
    void save();

private:
    //私有构造函数,防止外部通过 new 创建实例,确保单例特性
    explicit ConfigManager(QObject *parent = nullptr);

    //禁用拷贝构造函数
    ConfigManager(const ConfigManager &) = delete;

    //禁用赋值运算符
    ConfigManager &operator=(const ConfigManager &) = delete;

    //用于读写配置文件
    QSettings m_settings;
};

#endif // CONFIGMANAGER_H

// configmanager.cpp
#include "configmanager.h"

ConfigManager::ConfigManager(QObject *parent)
    : QObject(parent),
      m_settings("MyCompany", "SingletonDemo")
{
}

ConfigManager &ConfigManager::instance()
{
    static ConfigManager manager;
    return manager;
}

QString ConfigManager::userName() const
{
    return m_settings.value("user/name", "Guest").toString();
}

void ConfigManager::setUserName(const QString &name)
{
    m_settings.setValue("user/name", name);
}

void ConfigManager::save()
{
    m_settings.sync();
}

调用方式

在单例模式类中明确定义了静态成员函数static 单例模式类 &instance(); ,只有这样它才不属于某个对象(不允许new),而是属于类本身,此时统一以此方式调用这个类的成员函数:单例模式类::instance().xxx ,如下

复制代码
#include "configmanager.h"
void main(){
    QString userName=ConfigManager::instance().userName();
    QString newName="testname";
    ConfigManager::instance().setUserName(newName);
    ConfigManager::instance().save();
}

工厂方法模式

定义一个创建对象的接口,让子类决定实例化哪一个类,使创建过程延迟到子类进行。

特点

  • 创建和使用分离:业务代码不直接 new 具体类,而是调用工厂方法。
  • 面向抽象编程:返回基类指针、引用或智能指针,上层不依赖具体实现。
  • 统一创建入口:所有对象都通过工厂创建,便于集中管理初始化逻辑。
  • 隐藏细节:调用者不需要知道对象内部怎么构造、依赖哪些资源。

样例及说明

基类提供纯虚函数,子类重写虚函数,并且工厂子类创建具体对象,返回具体对象的智能指针。

工厂方法类实现

产品和工厂基类
复制代码
class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() = 0;
};

class ShapeFactory {
public:
    virtual ~ShapeFactory() = default;
    virtual std::unique_ptr<Shape> create() = 0;
};
产品和工厂子类

创建过程延迟到子类进行。

复制代码
//------------------------具体产品-----------------
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Draw Circle" << std::endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "Draw Rectangle" << std::endl;
    }
};

//------------------------具体工厂-----------------
class CircleFactory : public ShapeFactory {
public:
    std::unique_ptr<Shape> create() override {
        return std::make_unique<Circle>();//延迟到具体工厂创建具体对象
    }
};

class RectangleFactory : public ShapeFactory {
public:
    std::unique_ptr<Shape> create() override {
        return std::make_unique<Rectangle>();//延迟到具体工厂创建具体对象
    }
};

调用方法

先创建工厂之类,工厂之类在调用创建产品的函数,返回的是具体产品的智慧指针。

复制代码
std::unique_ptr<ShapeFactory> factory = std::make_unique<CircleFactory>();
auto shape = factory->create();
shape->draw();

策略模式

把"做什么"和"怎么做"拆开,用组合替代继承,让算法可以在运行时互换。

策略模式通常有三个角色:

  • ‌Strategy(策略接口)‌:虚拟函数接口类,定义了所有支持算法的公共接口。
  • ‌ConcreteStrategy(具体策略)‌:继承接口的子类,实现了具体的业务逻辑。
  • Context(上下文)‌:主框架,持有接口的指针,负责调用算法或行为。

关键点在于:Context 不关心具体是哪个策略,只认接口。

QT的低级插件QPlugin就是以策略模式构建,我之前分享的博文里面也专门介绍有:

【QT入门到晋级】低级插件QPlugin开发-CSDN博客

观察者模式

观察者模式本质是一对多的通知机制:一个对象状态变化时,自动通知所有关心它的对象,而不需要知道它们具体是谁。

QT的信号和槽机制和观察者模式契合度非常高,并且也支持一个信号发送,多个槽接收。

另外QT的事件系统也是这个机制。除了几个常用事件,需要捕获指定事件时,需要重写event()函数,根据事件类型触发相关的操作。

特点

角色 职责
Subject(被观察者) 维护观察者列表,提供注册/注销/通知接口
Observer(观察者) 定义统一的 update() 接口,接收通知
ConcreteSubject 具体被观察者,状态变化时调用 notify()
ConcreteObserver 具体观察者,实现 update() 响应逻辑

样例及说明

观察者基类提供纯虚函数;

观察者子类重写虚函数;

被观察者通过vector累加/减观察者,通过notify(通知)遍历vector逐个调用观察者子类重写的触发函数。

观察者模式类实现

观察者基类
复制代码
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const std::string &msg) = 0;
};
被观察者

attach/detach/notify 都可能被多线程并发调用,必须加锁。

attach(注册)、detach(注销)被直接对容器std::vector<std::shared_ptr<Observer>>进行增/删操作。

notify(通知)遍历vector逐个调用观察者子类重写的触发函数。

复制代码
class Subject {
public:
    void attach(std::shared_ptr<Observer> obs) {
        std::lock_guard<std::mutex> lock(mtx);
        observers.push_back(obs);
    }

    void detach(std::shared_ptr<Observer> obs) {
        std::lock_guard<std::mutex> lock(mtx);
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }

    void notify(const std::string &msg) {
        std::lock_guard<std::mutex> lock(mtx);
        for (auto &obs : observers) {
            obs->update(msg);
        }
    }

private:
    std::vector<std::shared_ptr<Observer>> observers;
    std::mutex mtx;
};
观察者子类

重写的触发函数update。

复制代码
class LogObserver : public Observer {
public:
    void update(const std::string &msg) override {
        std::cout << "[日志] " << msg << std::endl;
    }
};

class UIObserver : public Observer {
public:
    void update(const std::string &msg) override {
        std::cout << "[界面] 更新显示: " << msg << std::endl;
    }
};

调用方法

复制代码
int main() {
    Subject subject;

    auto log = std::make_shared<LogObserver>();
    auto ui  = std::make_shared<UIObserver>();

    subject.attach(log);
    subject.attach(ui);

    subject.notify("录像开始");   // 两个观察者都会收到
    subject.notify("录像结束");

    subject.detach(log);          // 移除日志观察者
    subject.notify("设备离线");   // 只有界面收到
}

适配器模式

在不修改原有类的前提下,把一个不兼容的接口转换成客户端期望的接口。适配器模式在数据中间件、API网关、流媒体、数据接入层等场景比较常见。

特点

中间件通常要连接很多不同系统:不同协议、不同数据格式、不同厂商 SDK、不同版本 API。适配器模式正好解决这种问题:

  • 把外部不兼容接口转换成内部统一接口;
  • 屏蔽第三方 SDK、协议、数据格式差异;
  • 新增数据源时,尽量不动核心业务代码;
  • 第三方接口升级、替换时,只改适配器,不改主流程。

样例及说明

适配器模式类实现

原有接口类
复制代码
class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string &msg) = 0;
    virtual void error(const std::string &msg) = 0;
};
第三接口类
复制代码
class FastLogger {
public:
    void writeLog(const char *msg, int level);
};
接口适配器类

在构造函数中转移右值,重写虚函数

复制代码
// 把 FastLogger 适配成 ILogger
class LoggerAdapter : public ILogger {//继承原有接口
public:
    explicit LoggerAdapter(std::unique_ptr<FastLogger> logger)//转移右值
        : logger_(std::move(logger)) {}

    void log(const std::string &msg) override {//重写虚函数
        logger_->writeLog(msg.c_str(), 0);
    }

    void error(const std::string &msg) override {//重写虚函数
        logger_->writeLog(msg.c_str(), 1);
    }

private:
    std::unique_ptr<FastLogger> logger_;
};

调用方法

复制代码
// 业务代码:只认 ILogger,完全不知道 FastLogger 的存在
class OrderService {
public:
    explicit OrderService(ILogger &logger) : logger_(logger) {}
    void createOrder() {
        logger_.log("creating order...");
        logger_.error("create order failed: insufficient stock");
    }
private:
    ILogger &logger_;
};

int main() {
    // 把第三方对象塞进适配器
    auto logger = std::make_unique<LoggerAdapter>(std::make_unique<FastLogger>());
    
    //方法一:自己直接调用
    logger->log("app started");//直接调
    logger->error("something went wrong");//直接调

    //方法二:把适配器"借"给业务类
    OrderService service(*logger);
    service.createOrder();
    return 0;
}
相关推荐
晚风予卿云月2 小时前
【C++高并发内存池】 2.ThreadCache设计
开发语言·c++·高并发内存池·tcmalloc
東隅已逝,桑榆非晚3 小时前
红黑树:从概念到实现
数据结构·c++·笔记·学习
她说..3 小时前
常见设计模式-观察者模式
java·spring boot·spring·观察者模式·设计模式
艾莉丝努力练剑3 小时前
【AI大模型接入SDK】ChatServer接口测试
网络·c++·人工智能·学习·ai·大模型·sdk
D e re k ZH4 小时前
C++ 模板与泛型编程进阶(No 2)
开发语言·c++
青少儿编程课堂4 小时前
蓝桥杯青少组图形化+信息素养+电子学会Scratch考级:校园口算闯关解析
c++·python·算法·bfs·信息学竞赛
eggwyw4 小时前
C++深入浅出探索数据结构的原理
开发语言·c++
eggwyw4 小时前
C++深入浅出讲解希尔排序算法的实现
开发语言·c++
ShineWinsu4 小时前
对于Qt:QProgressBar常见属性的解析
开发语言·c++·qt·进度条·gui·定时器·qprogressbar