【C++设计模式】第五篇:原型模式(Prototype)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

克隆对象的效率革命


1. 模式定义与用途​

核心思想​

  • 原型模式:通过复制现有对象(原型)来创建新对象,而非通过new构造。
  • 关键用途:
    1.减少初始化开销:适用于创建成本高的对象(如数据库连接)。
    2.动态配置对象:运行时通过克隆生成预设配置的实例。

经典场景​

  • 游戏开发:批量生成相同属性的敌人或道具。
  • 文档编辑:复制带格式的文本段落。

2. 模式结构解析​

UML类图

plaintext 复制代码
+---------------------+          +---------------------+  
|    Prototype        |          |       Client        |  
+---------------------+          +---------------------+  
| + clone(): Prototype|<>------->| - prototype: Prototype  
+---------------------+          +---------------------+  
          ^  
          |  
+---------------------+  
| ConcretePrototype   |  
+---------------------+  
| + clone()           |  
+---------------------+  

角色说明​

  • Prototype:抽象接口,声明克隆方法 clone()
  • ConcretePrototype:具体原型类,实现克隆逻辑。
  • Client:通过调用 clone() 创建新对象。

3. 简单示例:基础克隆实现

cpp 复制代码
#include <memory>  

// 抽象原型接口  
class Enemy {  
public:  
    virtual ~Enemy() = default;  
    virtual std::unique_ptr<Enemy> clone() const = 0;  
    virtual void attack() const = 0;  
};  

// 具体原型:骷髅战士  
class SkeletonWarrior : public Enemy {  
public:  
    std::unique_ptr<Enemy> clone() const override {  
        return std::make_unique<SkeletonWarrior>(*this); // 调用拷贝构造函数  
    }  

    void attack() const override {  
        std::cout << "骷髅战士挥舞骨刀!\n";  
    }  
};  

// 使用克隆  
auto original = std::make_unique<SkeletonWarrior>();  
auto clone = original->clone();  
clone->attack();  // 输出:骷髅战士挥舞骨刀!  

4. 完整代码:原型管理器与深拷贝优化

场景:游戏敌人原型注册与批量生成

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

// 抽象敌人原型  
class Enemy {  
public:  
    virtual ~Enemy() = default;  
    virtual std::unique_ptr<Enemy> clone() const = 0;  
    virtual void spawn() const = 0;  
    virtual void setHealth(int health) = 0;  
};  

// 具体原型:火焰恶魔  
class FireDemon : public Enemy {  
public:  
    FireDemon(int health, const std::string& color)  
        : health_(health), color_(color) {}  

    std::unique_ptr<Enemy> clone() const override {  
        return std::make_unique<FireDemon>(*this);  
    }  

    void spawn() const override {  
        std::cout << "生成" << color_ << "火焰恶魔(生命值:" << health_ << ")\n";  
    }  

    void setHealth(int health) override {  
        health_ = health;  
    }  

private:  
    int health_;  
    std::string color_;  
};  

// 原型管理器(注册表)  
class PrototypeRegistry {  
public:  
    void registerPrototype(const std::string& key, std::unique_ptr<Enemy> prototype) {  
        prototypes_[key] = std::move(prototype);  
    }  

    std::unique_ptr<Enemy> createEnemy(const std::string& key) {  
        auto it = prototypes_.find(key);  
        if (it != prototypes_.end()) {  
            return it->second->clone();  
        }  
        return nullptr;  
    }  

private:  
    std::unordered_map<std::string, std::unique_ptr<Enemy>> prototypes_;  
};  

// 客户端代码  
int main() {  
    PrototypeRegistry registry;  

    // 注册原型  
    registry.registerPrototype("red_demon", std::make_unique<FireDemon>(100, "红色"));  
    registry.registerPrototype("blue_demon", std::make_unique<FireDemon>(80, "蓝色"));  

    // 批量生成敌人  
    auto enemy1 = registry.createEnemy("red_demon");  
    auto enemy2 = registry.createEnemy("blue_demon");  
    auto enemy3 = registry.createEnemy("red_demon");  

    enemy1->spawn();  // 生成红色火焰恶魔(生命值:100)  
    enemy2->spawn();  // 生成蓝色火焰恶魔(生命值:80)  
    enemy3->setHealth(50);  
    enemy3->spawn();  // 生成红色火焰恶魔(生命值:50)  
}  

5. 优缺点分析

优点 ​​缺点
​避免重复初始化复杂对象 需正确处理深拷贝(尤其含指针成员时)
动态添加/删除原型配置 每个类需实现克隆方法,增加代码量
与工厂模式结合扩展性强 对简单对象可能得不偿失

6. 调试与优化策略

调试技巧(VS2022)​​

1.深拷贝验证

  • 在拷贝构造函数中设置断点,观察成员变量是否被正确复制。
  • 使用 内存断点 检测指针成员是否被重复释放。

2.​原型注册表检查

  • 输出注册表的键列表,确认原型是否成功注册。
cpp 复制代码
for (const auto& pair : prototypes_) {  
    std::cout << "已注册原型: " << pair.first << "\n";  
}  

性能优化​

1.原型预初始化

  • 在程序启动时预加载常用原型,减少运行时开销。

2.浅拷贝优化

  • 对只读数据成员使用浅拷贝(需确保生命周期安全)。
cpp 复制代码
class CheapToCopyEnemy : public Enemy {  
private:  
    const Texture* sharedTexture_;  // 只读资源,浅拷贝  
};  
相关推荐
Ayanami_Reii32 分钟前
区间不同数的个数-树状数组/线段树/莫队/主席树
数据结构·c++·算法·线段树·树状数组·主席树·莫队
大筒木老辈子1 小时前
C++笔记---并发支持库(atomic)
java·c++·笔记
zero_hz1 小时前
核心区分:用户态/内核态切换 vs. 程序阻塞
c++·io·内核态用户态
胡萝卜3.01 小时前
深入C++可调用对象:从function包装到bind参数适配的技术实现
开发语言·c++·人工智能·机器学习·bind·function·包装器
看见繁华1 小时前
C++ 高级
开发语言·c++
点云SLAM1 小时前
constexpr 和 explicit 在 C++ 中被提出的动机
开发语言·c++·explicit关键字·隐式转换·constexpr关键字·c++11/17/20
冷崖1 小时前
工厂模式-创建型
c++·设计模式
qq_310658512 小时前
mediasoup源码走读(六)——NetEQ
服务器·c++·音视频
qq_433554542 小时前
C++树形DP(树上分组背包)
c++·算法·深度优先
电子_咸鱼3 小时前
常见面试题——滑动窗口算法
c++·后端·python·算法·leetcode·哈希算法·推荐算法