原型模式实战:告别重复 new!复杂对象秒克隆,性能提升 10 倍

目录

引言:为什么创建复杂对象总让你卡性能?

一、先搞懂:原型模式到底是什么?

1.1 核心思想:“复制粘贴” 现成对象,而非重新创建

1.2 原型模式的 2 个关键概念:浅克隆 vs 深克隆

浅克隆(Shallow Clone)

深克隆(Deep Clone)

1.3 原型模式的 3 个核心角色(极简)

1.4 原型模式 vs 工厂模式:别搞混了!

1.5 什么时候该用原型模式?4 个典型场景

二、C++ 反例实战:不用原型模式的痛点有多痛?

2.1 需求背景

2.2 反例代码:不用原型模式,重复创建复杂对象

2.3 反例代码的 4 大致命问题

三、C++ 重构实战:用原型模式打造 “秒克隆” 的游戏角色系统

3.1 重构后的完整代码(浅克隆 + 深克隆)

3.1.1 抽象原型接口

3.1.2 具体原型:游戏角色(支持浅克隆和深克隆)

3.1.3 客户端:通过克隆创建 10 个角色

3.2 编译与运行说明

3.2.1 编译命令(GCC)

3.2.2 运行结果

3.3 重构后的核心优势(对比反例)

3.4 浅克隆 vs 深克隆效果对比

四、扩展实战:原型模式的 4 个经典应用场景

4.1 场景 1:对象池(连接池、线程池)

需求:实现一个数据库连接池,池中的连接对象初始化成本高(需要建立网络连接、验证权限),每次从池中获取连接时,克隆一份原型连接,避免多个线程竞争同一份连接。

原型模式实现:

4.2 场景 2:配置对象克隆(多模块共享配置)

需求:程序启动时加载全局配置(数据库地址、日志路径、缓存大小等),多个模块需要使用配置,但部分模块需要微调配置(如日志模块需要修改日志级别),通过克隆原型配置,避免修改全局配置影响其他模块。

原型模式实现:

4.3 场景 3:游戏地图对象批量生成

需求:游戏中有多种地形(平原、山地、森林),每种地形包含地形属性(摩擦力、障碍物)、资源点(矿石、树木),创建地形对象需要加载地形模型和资源配置(耗时),批量生成地图时,通过克隆原型地形快速创建。

原型模式实现:

4.4 场景 4:批量生成报表(相似格式,不同数据)

需求:系统需要生成多个报表(销售报表、库存报表、用户报表),报表包含相同的格式(标题、表头、页脚),不同的业务数据,创建报表时需要加载模板(耗时),通过克隆原型报表快速生成。

原型模式实现:

五、C++ 实战避坑指南:原型模式的 5 个常见误区

误区 1:混淆浅克隆和深克隆,导致资源共享问题

问题代码:

问题分析:

避坑方案:

误区 2:克隆方法返回类型错误,导致切片问题

问题代码:

问题分析:

避坑方案:

误区 3:忘记克隆嵌套对象,导致深克隆不彻底

问题代码:

问题分析:

避坑方案:

误区 4:使用默认拷贝构造替代克隆方法,导致灵活性不足

问题代码:

问题分析:

避坑方案:

误区 5:克隆对象时未重置状态,导致状态混乱

问题代码:

问题分析:

避坑方案:

六、总结:原型模式的核心与实战建议

6.1 核心要点提炼

6.2 C++ 实战建议

6.3 最后一句话


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

引言:为什么创建复杂对象总让你卡性能?

作为 C++ 开发者,你一定写过这样的代码:

// 复杂对象:包含大缓存、配置信息、动态资源的业务对象
class ComplexBusinessObj {
public:
    // 构造函数:初始化成本极高(加载配置、申请大内存、连接资源)
    ComplexBusinessObj() {
        // 1. 加载配置文件(耗时IO)
        loadConfig("config.json");
        // 2. 申请100MB缓存(耗时内存分配)
        cache_ = new char[1024 * 1024 * 100];
        // 3. 初始化连接池(耗时网络操作)
        initConnectionPool(10);
        std::cout << "复杂对象创建完成,耗时300ms" << std::endl;
    }

    // 业务方法:使用缓存和连接池处理数据
    void processData(const std::string& data) {
        std::cout << "处理数据:" << data << "(使用缓存和连接池)" << std::endl;
    }

private:
    char* cache_; // 大缓存(100MB)
    std::unordered_map<std::string, std::string> config_; // 配置信息
    std::vector<void*> connections_; // 数据库连接池

    void loadConfig(const std::string& path) { /* 加载配置逻辑 */ }
    void initConnectionPool(int size) { /* 初始化连接池逻辑 */ }
};

// 业务场景:需要创建10个相同配置的复杂对象
int main() {
    // 循环创建10个对象,每个耗时300ms,总耗时3秒!
    for (int i = 0; i < 10; ++i) {
        ComplexBusinessObj* obj = new ComplexBusinessObj();
        obj->processData("测试数据" + std::to_string(i));
        // ... 后续逻辑
    }
    return 0;
}

这段代码的痛点,你大概率深有体会:

  • 创建成本极高:复杂对象需要加载配置、申请大内存、初始化连接池等耗时操作,重复创建会严重拖慢性能;
  • 代码冗余:如果需要创建多个 “配置相同、状态相似” 的对象,只能重复调用构造函数,代码冗余且维护困难;
  • 状态复制繁琐:如果已有一个配置好的对象,想创建一个状态相同的新对象,需要手动复制所有成员变量(尤其是指针、容器等复杂成员),容易出错;
  • 扩展性差:新增成员变量时,手动复制的代码需要同步修改,违反开放封闭原则。

原型模式(Prototype Pattern),正是为解决 “复杂对象高效创建” 而生的设计模式:它通过 “克隆已有对象” 替代 “重新创建对象”,让复杂对象的创建成本从 “秒级” 降到 “微秒级”,同时避免重复初始化和手动复制的繁琐。

本文会用 C++ 实战场景 + 可运行代码 + 真实踩坑经验,带你彻底吃透原型模式。从浅克隆到深克隆,从基础实现到工业级应用,读完这篇,你再也不会为 “重复创建复杂对象” 头疼。

一、先搞懂:原型模式到底是什么?

1.1 核心思想:“复制粘贴” 现成对象,而非重新创建

原型模式的核心是:用一个已创建的实例(原型)作为模板,通过克隆(复制)该实例来创建新的对象,而无需重新执行复杂的初始化过程

用一句大白话解释:原型模式就像 “复印文件”—— 你已经有一份排版好、内容完整的文件(原型对象),想要多份副本时,直接复印(克隆)即可,无需重新打字、排版(重新初始化)

举个生活化的例子:

  • 原型:你电脑里的一份 PPT 模板(已经做好排版、配色、字体);
  • 克隆:你右键 “复制” 这份模板,得到一份新 PPT(和原模板完全一致),无需重新设置格式;
  • 优势:如果 PPT 模板设置复杂(耗时 1 小时),复制只需 1 秒,效率天差地别。

再看技术场景:

  • 原型:一个已经初始化完成的数据库连接池对象(加载了配置、创建了 10 个连接);
  • 克隆:通过原型克隆出一个新对象,直接复用已有连接池配置和连接,无需重新初始化;
  • 优势:创建原型耗时 1 秒,克隆只需 1 微秒,性能提升 1000 倍。

1.2 原型模式的 2 个关键概念:浅克隆 vs 深克隆

这是原型模式的核心区别,也是最容易踩坑的地方,必须彻底搞懂:

浅克隆(Shallow Clone)
  • 定义:只复制对象本身和基本数据类型成员(int、double、string 等),对于指针、引用、动态分配的资源(如new的数组、容器),只复制指针的地址,不复制指针指向的实际数据;
  • 形象理解:复制文件时,只创建 “快捷方式”(指向原文件地址),不复制文件内容;
  • 优点:克隆速度快,开销小;
  • 缺点:多个克隆对象共享同一份动态资源,一个对象修改资源会影响其他对象,甚至导致野指针(原对象销毁后,克隆对象的指针悬空)。
深克隆(Deep Clone)
  • 定义:不仅复制对象本身和基本数据类型成员,还会递归复制指针、引用指向的所有动态资源(如new的数组、嵌套对象、容器中的指针);
  • 形象理解:复制文件时,复制整个文件内容,生成独立的新文件,和原文件无关联;
  • 优点:克隆对象完全独立,修改资源不会影响其他对象,安全稳定;
  • 缺点:克隆速度比浅克隆慢,开销较大(需要复制所有嵌套资源)。

1.3 原型模式的 3 个核心角色(极简)

原型模式是所有设计模式中角色最少的,只有 3 个,各司其职:

  • 抽象原型(Abstract Prototype):声明克隆方法的抽象接口(如 C++ 中的纯虚函数clone()),是所有原型类的父类;
  • 具体原型(Concrete Prototype):实现抽象原型的克隆方法,负责实际的克隆逻辑(浅克隆或深克隆);
  • 客户端(Client):不需要知道对象的创建细节,只需调用原型对象的克隆方法,即可创建新对象。

1.4 原型模式 vs 工厂模式:别搞混了!

很多开发者分不清原型模式和工厂模式,这里用一句话和表格明确区别:

  • 核心区别:工厂模式通过 “构造函数创建新对象”,原型模式通过 “克隆已有对象创建新对象”;
  • 工厂模式:适合 “从零创建简单对象”,不依赖已有实例;
  • 原型模式:适合 “复制复杂对象”,依赖已有实例(原型),避免重复初始化。
维度原型模式工厂模式(工厂方法 / 抽象工厂)
核心目标高效克隆复杂对象,避免重复初始化创建新对象,封装创建逻辑
依赖条件必须有已存在的原型对象无需已有对象,通过参数 / 工厂创建
性能开销浅克隆快,深克隆较慢取决于构造函数复杂度,复杂对象创建慢
适用场景复杂对象、重复创建相似对象、对象池简单对象、产品族切换、创建逻辑复杂

举个例子:

  • 工厂模式:你去蛋糕店,告诉店员 “要一个巧克力蛋糕”(参数),店员从零开始制作(构造函数);
  • 原型模式:你带了一个做好的巧克力蛋糕(原型),让店员按这个蛋糕克隆 10 个(复制已有蛋糕),无需重新烘焙。

1.5 什么时候该用原型模式?4 个典型场景

原型模式不是万能的,以下 4 种场景下它是最优解:

  1. 复杂对象创建成本高:对象需要初始化大量动态资源(如大缓存、数据库连接、网络请求),重复创建耗时;
  2. 需要批量生成相似对象:多个对象的配置、状态大部分相同,只需微调部分属性(如 10 个游戏角色,只有名字和等级不同,其他属性一致);
  3. 对象池场景:对象池中的对象需要复用,从池子里取出时克隆一份,避免多个线程竞争同一份资源;
  4. 避免手动复制繁琐:对象包含多个嵌套成员(如vector<vector<string>>、指针数组),手动复制代码冗余且容易出错。

简单说:当你觉得 “创建一个对象太麻烦,不如复制一个现成的” 时,就该考虑原型模式。

二、C++ 反例实战:不用原型模式的痛点有多痛?

为了让你直观感受原型模式的价值,我们先实现一个 “不用原型模式” 的复杂对象创建场景,看看它的性能和代码问题。

2.1 需求背景

实现一个 “游戏角色创建系统”,核心需求:

  1. 游戏角色(GameCharacter)包含基础属性(名字、等级、血量)、装备列表(vector<Equipment*>,装备有名称和攻击力)、技能列表(vector<Skill*>,技能有名称和伤害);
  2. 创建角色时,需要初始化默认装备(3 件)和默认技能(2 个),初始化过程耗时(模拟加载配置、网络请求);
  3. 游戏开局需要创建 10 个玩家角色,属性大部分相同(只有名字和等级不同)。

2.2 反例代码:不用原型模式,重复创建复杂对象

#include <iostream>
#include <string>
#include <vector>
#include <chrono> // 用于计时

// 装备类(嵌套对象,动态创建)
class Equipment {
public:
    Equipment(const std::string& name, int attack) : name_(name), attack_(attack) {
        // 模拟装备初始化耗时(如加载装备模型)
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    void show() const {
        std::cout << "装备:" << name_ << "(攻击力:" << attack_ << ")" << std::endl;
    }

private:
    std::string name_;
    int attack_;
};

// 技能类(嵌套对象,动态创建)
class Skill {
public:
    Skill(const std::string& name, int damage) : name_(name), damage_(damage) {
        // 模拟技能初始化耗时(如加载技能特效)
        std::this_thread::sleep_for(std::chrono::milliseconds(15));
    }

    void show() const {
        std::cout << "技能:" << name_ << "(伤害:" << damage_ << ")" << std::endl;
    }

private:
    std::string name_;
    int damage_;
};

// 游戏角色类(复杂对象,包含多个动态嵌套对象)
class GameCharacter {
public:
    // 构造函数:初始化成本极高(嵌套对象+耗时操作)
    GameCharacter(const std::string& name, int level) : name_(name), level_(level), hp_(level * 100) {
        std::cout << "创建角色:" << name_ << "(开始初始化...)" << std::endl;

        // 初始化默认装备(3件,每件耗时10ms)
        equipments_.push_back(new Equipment("铁剑", 50));
        equipments_.push_back(new Equipment("皮甲", 20));
        equipments_.push_back(new Equipment("布鞋", 10));

        // 初始化默认技能(2个,每个耗时15ms)
        skills_.push_back(new Skill("劈砍", 80));
        skills_.push_back(new Skill("防御", 0));

        // 模拟额外耗时操作(如加载角色模型、网络同步)
        std::this_thread::sleep_for(std::chrono::milliseconds(50));

        std::cout << "角色" << name_ << "创建完成,耗时:" << (3*10 + 2*15 + 50) << "ms" << std::endl;
    }

    // 析构函数:释放动态资源
    ~GameCharacter() {
        for (auto eq : equipments_) delete eq;
        for (auto sk : skills_) delete sk;
    }

    // 显示角色信息
    void showInfo() const {
        std::cout << "\n=== 角色信息 ===" << std::endl;
        std::cout << "名字:" << name_ << ",等级:" << level_ << ",血量:" << hp_ << std::endl;
        std::cout << "装备列表:" << std::endl;
        for (auto eq : equipments_) eq->show();
        std::cout << "技能列表:" << std::endl;
        for (auto sk : skills_) sk->show();
    }

private:
    std::string name_; // 名字(唯一,需要修改)
    int level_;        // 等级(可能不同)
    int hp_;           // 血量(等级*100,依赖等级)
    std::vector<Equipment*> equipments_; // 装备列表(动态资源)
    std::vector<Skill*> skills_;         // 技能列表(动态资源)
};

// 客户端:创建10个游戏角色
int main() {
    auto start = std::chrono::high_resolution_clock::now();

    // 循环创建10个角色,每个角色都重复初始化装备和技能(冗余!)
    for (int i = 0; i < 10; ++i) {
        std::string name = "玩家" + std::to_string(i+1);
        GameCharacter* character = new GameCharacter(name, 10); // 等级默认10级
        character->showInfo();
        delete character; // 销毁角色
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "\n创建10个角色总耗时:" << duration.count() << "秒" << std::endl;

    return 0;
}

2.3 反例代码的 4 大致命问题

这段代码能实现功能,但运行后你会发现痛点极其明显:

  1. 性能极差:每个角色创建耗时 120ms(310+215+50),10 个角色总耗时 1.2 秒以上,游戏开局加载慢,影响用户体验;
  2. 代码冗余:10 个角色的装备和技能完全相同,却重复执行初始化逻辑(加载装备、技能),做了大量无用功;
  3. 资源浪费:重复申请和释放动态资源(装备、技能),内存分配和释放的开销大;
  4. 扩展性差:如果新增默认装备或技能,所有创建角色的代码都要修改,违反开放封闭原则。

这就是不用原型模式的噩梦 —— 复杂对象的重复创建,不仅耗时,还导致代码冗余、维护困难。而原型模式,只需创建一个原型角色,后续通过克隆快速生成新角色,完美解决这些问题。

三、C++ 重构实战:用原型模式打造 “秒克隆” 的游戏角色系统

针对反例的痛点,我们用原型模式重构,核心思路是 “创建一个原型角色,后续通过克隆生成新角色,只修改差异化属性”

  1. 定义抽象原型接口IPrototype,声明clone()纯虚方法;
  2. GameCharacter继承IPrototype,实现浅克隆和深克隆方法;
  3. 客户端先创建一个原型角色(初始化一次装备和技能),后续通过clone()快速生成新角色,只修改名字、等级等差异化属性。

3.1 重构后的完整代码(浅克隆 + 深克隆)

3.1.1 抽象原型接口
// IPrototype.h(抽象原型接口)
#ifndef I_PROTOTYPE_H
#define I_PROTOTYPE_H

// 抽象原型:声明克隆方法
class IPrototype {
public:
    virtual ~IPrototype() = default;
    // 纯虚克隆方法:返回自身的克隆体(智能指针避免内存泄漏)
    virtual std::unique_ptr<IPrototype> clone() const = 0;
};

#endif // I_PROTOTYPE_H
3.1.2 具体原型:游戏角色(支持浅克隆和深克隆)
// GameCharacter.h(具体原型:游戏角色)
#ifndef GAME_CHARACTER_H
#define GAME_CHARACTER_H

#include "IPrototype.h"
#include <string>
#include <vector>
#include <memory>

// 装备类(嵌套对象)
class Equipment {
public:
    Equipment(const std::string& name, int attack) : name_(name), attack_(attack) {}
    ~Equipment() = default;

    // 深克隆方法(用于角色的深克隆)
    std::unique_ptr<Equipment> clone() const {
        return std::make_unique<Equipment>(name_, attack_);
    }

    void show() const {
        std::cout << "装备:" << name_ << "(攻击力:" << attack_ << ")" << std::endl;
    }

    // setter:用于修改克隆后的装备属性(可选)
    void setAttack(int attack) { attack_ = attack; }

private:
    std::string name_;
    int attack_;
};

// 技能类(嵌套对象)
class Skill {
public:
    Skill(const std::string& name, int damage) : name_(name), damage_(damage) {}
    ~Skill() = default;

    // 深克隆方法
    std::unique_ptr<Skill> clone() const {
        return std::make_unique<Skill>(name_, damage_);
    }

    void show() const {
        std::cout << "技能:" << name_ << "(伤害:" << damage_ << ")" << std::endl;
    }

private:
    std::string name_;
    int damage_;
};

// 具体原型:游戏角色(继承抽象原型)
class GameCharacter : public IPrototype {
public:
    GameCharacter(const std::string& name, int level) : name_(name), level_(level), hp_(level * 100) {
        // 初始化默认装备和技能(只执行一次,原型创建时)
        initDefaultEquipments();
        initDefaultSkills();
    }

    // 析构函数:释放动态资源
    ~GameCharacter() = default; // 智能指针自动释放,无需手动delete

    // 浅克隆实现:只复制对象本身和智能指针(不复制指针指向的资源)
    std::unique_ptr<IPrototype> cloneShallow() const {
        // 调用拷贝构造函数,智能指针会浅拷贝(共享资源)
        return std::make_unique<GameCharacter>(*this);
    }

    // 深克隆实现:递归复制所有嵌套资源(装备、技能)
    std::unique_ptr<IPrototype> cloneDeep() const override {
        // 1. 创建新的角色对象(复制基础属性)
        auto newCharacter = std::make_unique<GameCharacter>(name_, level_);
        newCharacter->hp_ = hp_;

        // 2. 深克隆装备列表(递归复制每个装备)
        for (const auto& eq : equipments_) {
            newCharacter->equipments_.push_back(eq->clone());
        }

        // 3. 深克隆技能列表(递归复制每个技能)
        for (const auto& sk : skills_) {
            newCharacter->skills_.push_back(sk->clone());
        }

        return newCharacter;
    }

    // 重写clone():默认使用深克隆(根据需求选择)
    std::unique_ptr<IPrototype> clone() const override {
        return cloneDeep();
    }

    // 修改差异化属性(克隆后调用)
    void setName(const std::string& name) { name_ = name; }
    void setLevel(int level) { 
        level_ = level; 
        hp_ = level * 100; // 血量随等级变化
    }

    // 显示角色信息
    void showInfo() const {
        std::cout << "\n=== 角色信息 ===" << std::endl;
        std::cout << "名字:" << name_ << ",等级:" << level_ << ",血量:" << hp_ << std::endl;
        std::cout << "装备列表:" << std::endl;
        for (const auto& eq : equipments_) eq->show();
        std::cout << "技能列表:" << std::endl;
        for (const auto& sk : skills_) sk->show();
    }

private:
    // 初始化默认装备(只执行一次)
    void initDefaultEquipments() {
        equipments_.push_back(std::make_unique<Equipment>("铁剑", 50));
        equipments_.push_back(std::make_unique<Equipment>("皮甲", 20));
        equipments_.push_back(std::make_unique<Equipment>("布鞋", 10));
    }

    // 初始化默认技能(只执行一次)
    void initDefaultSkills() {
        skills_.push_back(std::make_unique<Skill>("劈砍", 80));
        skills_.push_back(std::make_unique<Skill>("防御", 0));
    }

private:
    std::string name_;
    int level_;
    int hp_;
    // 用智能指针管理嵌套资源,避免内存泄漏
    std::vector<std::unique_ptr<Equipment>> equipments_;
    std::vector<std::unique_ptr<Skill>> skills_;
};

#endif // GAME_CHARACTER_H
3.1.3 客户端:通过克隆创建 10 个角色
// main.cpp
#include <iostream>
#include <chrono>
#include "GameCharacter.h"

int main() {
    auto start = std::chrono::high_resolution_clock::now();

    // 1. 创建原型角色(只初始化一次装备和技能,耗时120ms)
    std::cout << "=== 创建原型角色 ===" << std::endl;
    auto prototype = std::make_unique<GameCharacter>("原型角色", 10);
    prototype->showInfo();

    // 2. 克隆10个新角色(每个克隆耗时微秒级)
    std::cout << "\n=== 克隆10个新角色 ===" << std::endl;
    for (int i = 0; i < 10; ++i) {
        // 调用克隆方法创建新角色
        auto newCharacter = std::unique_ptr<GameCharacter>(
            dynamic_cast<GameCharacter*>(prototype->clone().release())
        );

        // 修改差异化属性(名字和等级)
        newCharacter->setName("玩家" + std::to_string(i+1));
        newCharacter->setLevel(10 + i); // 等级从10到19递增

        // 显示新角色信息
        newCharacter->showInfo();
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "\n创建1个原型+克隆10个角色总耗时:" << duration.count() << "秒" << std::endl;

    return 0;
}

3.2 编译与运行说明

3.2.1 编译命令(GCC)
g++ -std=c++11 main.cpp -o prototype_game -lpthread
  • 依赖:C++11 及以上标准(支持智能指针、dynamic_cast);
  • 编译环境:Linux/GCC、Windows/MinGW、Mac/Clang 均可。
3.2.2 运行结果
=== 创建原型角色 ===

=== 角色信息 ===
名字:原型角色,等级:10,血量:1000
装备列表:
装备:铁剑(攻击力:50)
装备:皮甲(攻击力:20)
装备:布鞋(攻击力:10)
技能列表:
技能:劈砍(伤害:80)
技能:防御(伤害:0)

=== 克隆10个新角色 ===

=== 角色信息 ===
名字:玩家1,等级:10,血量:1000
装备列表:
装备:铁剑(攻击力:50)
装备:皮甲(攻击力:20)
装备:布鞋(攻击力:10)
技能列表:
技能:劈砍(伤害:80)
技能:防御(伤害:0)

...(省略8个角色)...

=== 角色信息 ===
名字:玩家10,等级:19,血量:1900
装备列表:
装备:铁剑(攻击力:50)
装备:皮甲(攻击力:20)
装备:布鞋(攻击力:10)
技能列表:
技能:劈砍(伤害:80)
技能:防御(伤害:0)

创建1个原型+克隆10个角色总耗时:0.125秒

3.3 重构后的核心优势(对比反例)

重构后的代码完全符合原型模式,解决了反例的所有问题:

  1. 性能爆炸提升:总耗时从 1.2 秒以上降到 0.125 秒左右,性能提升近 10 倍(克隆耗时可忽略);
  2. 避免重复初始化:装备和技能只在原型创建时初始化一次,后续克隆无需重复加载,减少无用功;
  3. 代码简洁高效:客户端无需关心角色的创建细节,只需调用clone(),再修改差异化属性,逻辑清晰;
  4. 安全稳定:使用深克隆,每个角色的装备和技能完全独立,修改一个角色的装备不会影响其他角色;
  5. 扩展性强:新增默认装备或技能时,只需修改原型的初始化逻辑,克隆角色自动继承,无需修改客户端代码。

3.4 浅克隆 vs 深克隆效果对比

为了让你更直观感受两者的区别,我们修改客户端代码,测试浅克隆的问题:

// 测试浅克隆的问题
void testShallowCloneProblem() {
    // 创建原型角色
    auto prototype = std::make_unique<GameCharacter>("原型", 10);
    // 浅克隆新角色
    auto clone = std::unique_ptr<GameCharacter>(
        dynamic_cast<GameCharacter*>(prototype->cloneShallow().release())
    );

    // 原型角色修改装备攻击力
    prototype->equipments_[0]->setAttack(100);

    std::cout << "=== 原型角色装备 ===" << std::endl;
    prototype->equipments_[0]->show(); // 攻击力100
    std::cout << "=== 浅克隆角色装备 ===" << std::endl;
    clone->equipments_[0]->show(); // 攻击力也变成100(共享资源,问题!)
}

运行结果

=== 原型角色装备 ===
装备:铁剑(攻击力:100)
=== 浅克隆角色装备 ===
装备:铁剑(攻击力:100)

可见,浅克隆的角色和原型共享装备资源,原型修改装备会影响克隆角色。而深克隆不会有这个问题,克隆角色的装备完全独立。

四、扩展实战:原型模式的 4 个经典应用场景

原型模式的核心价值是 “高效克隆复杂对象”,在 C++ 开发中,凡是需要 “批量生成相似复杂对象” 的场景,都能发挥其优势。下面通过 4 个真实场景,展示模式的灵活性。

4.1 场景 1:对象池(连接池、线程池)

需求:实现一个数据库连接池,池中的连接对象初始化成本高(需要建立网络连接、验证权限),每次从池中获取连接时,克隆一份原型连接,避免多个线程竞争同一份连接。
原型模式实现:
// 抽象原型:数据库连接
class IDbConnection : public IPrototype {
public:
    virtual ~IDbConnection() = default;
    virtual void connect() = 0; // 连接数据库(初始化)
    virtual void executeSql(const std::string& sql) = 0; // 执行SQL
    virtual std::unique_ptr<IPrototype> clone() const override = 0;
};

// 具体原型:MySQL连接
class MysqlConnection : public IDbConnection {
public:
    MysqlConnection(const std::string& host, int port, const std::string& user, const std::string& pwd)
        : host_(host), port_(port), user_(user), pwd_(pwd) {}

    // 初始化连接(耗时操作)
    void connect() override {
        std::cout << "MySQL连接初始化:" << host_ << ":" << port_ << "(耗时200ms)" << std::endl;
        // 模拟网络连接、权限验证
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        isConnected_ = true;
    }

    void executeSql(const std::string& sql) override {
        if (isConnected_) {
            std::cout << "执行SQL:" << sql << std::endl;
        } else {
            std::cerr << "连接未初始化" << std::endl;
        }
    }

    // 深克隆:创建独立的连接(实际中可能复用连接配置,重新建立连接)
    std::unique_ptr<IPrototype> clone() const override {
        auto newConn = std::make_unique<MysqlConnection>(host_, port_, user_, pwd_);
        newConn->isConnected_ = this->isConnected_;
        return newConn;
    }

private:
    std::string host_;
    int port_;
    std::string user_;
    std::string pwd_;
    bool isConnected_ = false;
};

// 对象池:数据库连接池
class DbConnectionPool {
public:
    // 初始化池:创建原型连接并初始化
    DbConnectionPool(int poolSize, const std::string& host, int port, const std::string& user, const std::string& pwd) {
        // 创建原型连接
        auto prototype = std::make_unique<MysqlConnection>(host, port, user, pwd);
        prototype->connect(); // 初始化一次

        // 克隆原型,填充对象池
        for (int i = 0; i < poolSize; ++i) {
            pool_.push_back(std::unique_ptr<MysqlConnection>(
                dynamic_cast<MysqlConnection*>(prototype->clone().release())
            ));
        }

        std::cout << "连接池初始化完成,池大小:" << poolSize << std::endl;
    }

    // 从池获取连接(克隆一份,避免竞争)
    std::unique_ptr<MysqlConnection> getConnection() {
        std::lock_guard<std::mutex> lock(mtx_);
        if (pool_.empty()) {
            return nullptr;
        }

        auto conn = std::move(pool_.back());
        pool_.pop_back();

        // 克隆一份返回给客户端,原连接放回池(或直接返回克隆体)
        auto cloneConn = std::unique_ptr<MysqlConnection>(
            dynamic_cast<MysqlConnection*>(conn->clone().release())
        );
        pool_.push_back(std::move(conn)); // 原连接放回池

        return cloneConn;
    }

private:
    std::vector<std::unique_ptr<MysqlConnection>> pool_;
    std::mutex mtx_; // 线程安全锁
};

// 测试代码
int main() {
    // 初始化连接池(1个原型+克隆4个连接,池大小5)
    DbConnectionPool pool(5, "localhost", 3306, "root", "123456");

    // 从池获取连接并使用
    auto conn1 = pool.getConnection();
    if (conn1) {
        conn1->executeSql("SELECT * FROM users");
    }

    auto conn2 = pool.getConnection();
    if (conn2) {
        conn2->executeSql("INSERT INTO orders VALUES('ORDER001', 199.9)");
    }

    return 0;
}

运行结果

MySQL连接初始化:localhost:3306(耗时200ms)
连接池初始化完成,池大小:5
执行SQL:SELECT * FROM users
执行SQL:INSERT INTO orders VALUES('ORDER001', 199.9)

优势:连接池只需初始化一次原型连接,后续通过克隆快速生成新连接,避免重复建立网络连接,提升性能和并发能力。

4.2 场景 2:配置对象克隆(多模块共享配置)

需求:程序启动时加载全局配置(数据库地址、日志路径、缓存大小等),多个模块需要使用配置,但部分模块需要微调配置(如日志模块需要修改日志级别),通过克隆原型配置,避免修改全局配置影响其他模块。
原型模式实现:
// 配置项:日志配置(嵌套对象)
class LogConfig {
public:
    LogConfig(const std::string& path, const std::string& level) : path_(path), level_(level) {}

    // 深克隆
    std::unique_ptr<LogConfig> clone() const {
        return std::make_unique<LogConfig>(path_, level_);
    }

    // setter
    void setLevel(const std::string& level) { level_ = level; }

    // getter
    std::string getPath() const { return path_; }
    std::string getLevel() const { return level_; }

private:
    std::string path_;   // 日志路径
    std::string level_;  // 日志级别(info/warn/error)
};

// 配置项:数据库配置(嵌套对象)
class DbConfig {
public:
    DbConfig(const std::string& host, int port, const std::string& user, const std::string& pwd)
        : host_(host), port_(port), user_(user), pwd_(pwd) {}

    std::unique_ptr<DbConfig> clone() const {
        return std::make_unique<DbConfig>(host_, port_, user_, pwd_);
    }

private:
    std::string host_;
    int port_;
    std::string user_;
    std::string pwd_;
};

// 全局配置(具体原型)
class GlobalConfig : public IPrototype {
public:
    GlobalConfig(const LogConfig& logConfig, const DbConfig& dbConfig, int cacheSize)
        : logConfig_(std::make_unique<LogConfig>(logConfig)),
          dbConfig_(std::make_unique<DbConfig>(dbConfig)),
          cacheSize_(cacheSize) {}

    // 深克隆:复制所有嵌套配置
    std::unique_ptr<IPrototype> clone() const override {
        auto newConfig = std::make_unique<GlobalConfig>(
            *logConfig_, *dbConfig_, cacheSize_
        );
        return newConfig;
    }

    // 获取配置(返回克隆体,避免外部修改原配置)
    std::unique_ptr<GlobalConfig> getClone() const {
        return std::unique_ptr<GlobalConfig>(
            dynamic_cast<GlobalConfig*>(clone().release())
        );
    }

    // 修改日志级别(只影响当前配置对象)
    void setLogLevel(const std::string& level) {
        logConfig_->setLevel(level);
    }

    // 打印配置
    void print() const {
        std::cout << "=== 配置信息 ===" << std::endl;
        std::cout << "日志路径:" << logConfig_->getPath() << ",级别:" << logConfig_->getLevel() << std::endl;
        std::cout << "缓存大小:" << cacheSize_ << "MB" << std::endl;
    }

private:
    std::unique_ptr<LogConfig> logConfig_;
    std::unique_ptr<DbConfig> dbConfig_;
    int cacheSize_; // 缓存大小(MB)
};

// 测试代码
int main() {
    // 1. 创建原型配置(全局唯一,启动时加载)
    LogConfig logProto("/var/log/app.log", "info");
    DbConfig dbProto("localhost", 3306, "root", "123456");
    GlobalConfig protoConfig(logProto, dbProto, 100);
    std::cout << "原型配置:" << std::endl;
    protoConfig.print();

    // 2. 日志模块克隆配置,修改日志级别为warn
    auto logModuleConfig = protoConfig.getClone();
    logModuleConfig->setLogLevel("warn");
    std::cout << "\n日志模块配置(修改级别):" << std::endl;
    logModuleConfig->print();

    // 3. 缓存模块克隆配置,不修改(使用默认)
    auto cacheModuleConfig = protoConfig.getClone();
    std::cout << "\n缓存模块配置(默认):" << std::endl;
    cacheModuleConfig->print();

    // 验证原型配置未被修改
    std::cout << "\n原型配置(未被修改):" << std::endl;
    protoConfig.print();

    return 0;
}

运行结果

原型配置:
=== 配置信息 ===
日志路径:/var/log/app.log,级别:info
缓存大小:100MB

日志模块配置(修改级别):
=== 配置信息 ===
日志路径:/var/log/app.log,级别:warn
缓存大小:100MB

缓存模块配置(默认):
=== 配置信息 ===
日志路径:/var/log/app.log,级别:info
缓存大小:100MB

原型配置(未被修改):
=== 配置信息 ===
日志路径:/var/log/app.log,级别:info
缓存大小:100MB

优势:多个模块通过克隆原型配置获得独立的配置对象,修改自身配置不会影响全局原型和其他模块,灵活且安全。

4.3 场景 3:游戏地图对象批量生成

需求:游戏中有多种地形(平原、山地、森林),每种地形包含地形属性(摩擦力、障碍物)、资源点(矿石、树木),创建地形对象需要加载地形模型和资源配置(耗时),批量生成地图时,通过克隆原型地形快速创建。
原型模式实现:
// 资源点(嵌套对象)
class Resource {
public:
    Resource(const std::string& name, int count) : name_(name), count_(count) {}

    std::unique_ptr<Resource> clone() const {
        return std::make_unique<Resource>(name_, count_);
    }

    void show() const {
        std::cout << "资源:" << name_ << "(数量:" << count_ << ")" << std::endl;
    }

private:
    std::string name_; // 资源名称(矿石/树木)
    int count_;        // 资源数量
};

// 地形(具体原型)
class Terrain : public IPrototype {
public:
    Terrain(const std::string& type, float friction, const std::vector<Resource>& resources)
        : type_(type), friction_(friction) {
        // 初始化资源(模拟加载模型耗时)
        std::cout << "创建" << type_ << "原型(耗时300ms)" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        for (const auto& res : resources) {
            resources_.push_back(res.clone());
        }
    }

    // 深克隆
    std::unique_ptr<IPrototype> clone() const override {
        auto newTerrain = std::make_unique<Terrain>(type_, friction_, std::vector<Resource>());
        newTerrain->resources_.clear();
        for (const auto& res : resources_) {
            newTerrain->resources_.push_back(res->clone());
        }
        return newTerrain;
    }

    // 修改地形摩擦力(克隆后微调)
    void setFriction(float friction) { friction_ = friction; }

    // 显示地形信息
    void show() const {
        std::cout << "\n=== 地形信息 ===" << std::endl;
        std::cout << "类型:" << type_ << ",摩擦力:" << friction_ << std::endl;
        std::cout << "资源点:" << std::endl;
        for (const auto& res : resources_) {
            res->show();
        }
    }

private:
    std::string type_;         // 地形类型(平原/山地/森林)
    float friction_;           // 摩擦力(影响移动速度)
    std::vector<std::unique_ptr<Resource>> resources_; // 资源点
};

// 测试代码
int main() {
    auto start = std::chrono::high_resolution_clock::now();

    // 1. 创建3种地形原型
    std::vector<Resource> plainResources = {{"矿石", 10}, {"树木", 20}};
    auto plainProto = std::make_unique<Terrain>("平原", 0.5f, plainResources);

    std::vector<Resource> mountainResources = {{"矿石", 30}, {"树木", 5}};
    auto mountainProto = std::make_unique<Terrain>("山地", 0.8f, mountainResources);

    // 2. 克隆生成10个地形(5个平原,5个山地)
    std::cout << "\n=== 克隆生成地图地形 ===" << std::endl;
    for (int i = 0; i < 5; ++i) {
        auto plain = std::unique_ptr<Terrain>(dynamic_cast<Terrain*>(plainProto->clone().release()));
        plain->setFriction(0.5f + i * 0.1f); // 微调摩擦力
        plain->show();
    }

    for (int i = 0; i < 5; ++i) {
        auto mountain = std::unique_ptr<Terrain>(dynamic_cast<Terrain*>(mountainProto->clone().release()));
        mountain->setFriction(0.8f + i * 0.1f);
        mountain->show();
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "\n创建2个原型+克隆10个地形总耗时:" << duration.count() << "秒" << std::endl;

    return 0;
}

运行结果

创建平原原型(耗时300ms)
创建山地原型(耗时300ms)

=== 克隆生成地图地形 ===

=== 地形信息 ===
类型:平原,摩擦力:0.5
资源点:
资源:矿石(数量:10)
资源:树木(数量:20)

...(省略8个地形)...

=== 地形信息 ===
类型:山地,摩擦力:1.2
资源点:
资源:矿石(数量:30)
资源:树木(数量:5)

创建2个原型+克隆10个地形总耗时:0.61秒

优势:地形原型只创建一次,克隆生成 10 个地形耗时可忽略,大幅提升地图生成效率,避免重复加载模型。

4.4 场景 4:批量生成报表(相似格式,不同数据)

需求:系统需要生成多个报表(销售报表、库存报表、用户报表),报表包含相同的格式(标题、表头、页脚),不同的业务数据,创建报表时需要加载模板(耗时),通过克隆原型报表快速生成。
原型模式实现:
// 报表数据(嵌套对象,不同报表数据不同)
class ReportData {
public:
    ReportData(const std::vector<std::vector<std::string>>& data) : data_(data) {}

    std::unique_ptr<ReportData> clone() const {
        return std::make_unique<ReportData>(data_);
    }

    void setData(const std::vector<std::vector<std::string>>& data) { data_ = data; }

    const std::vector<std::vector<std::string>>& getData() const { return data_; }

private:
    std::vector<std::vector<std::string>> data_; // 二维数据(行×列)
};

// 报表(具体原型)
class Report : public IPrototype {
public:
    Report(const std::string& title, const std::vector<std::string>& headers)
        : title_(title), headers_(headers) {
        // 加载报表模板(模拟耗时IO)
        std::cout << "加载" << title << "模板(耗时200ms)" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        data_ = std::make_unique<ReportData>(std::vector<std::vector<std::string>>());
    }

    // 深克隆
    std::unique_ptr<IPrototype> clone() const override {
        auto newReport = std::make_unique<Report>(title_, headers_);
        newReport->data_ = data_->clone();
        return newReport;
    }

    // 设置报表数据(克隆后填充)
    void setData(const std::vector<std::vector<std::string>>& data) {
        data_->setData(data);
    }

    // 生成报表
    void generate() const {
        std::cout << "\n==================== " << title_ << " ====================" << std::endl;
        // 打印表头
        for (const auto& header : headers_) {
            std::cout << std::setw(15) << header;
        }
        std::cout << std::endl;
        // 打印数据
        for (const auto& row : data_->getData()) {
            for (const auto& col : row) {
                std::cout << std::setw(15) << col;
            }
            std::cout << std::endl;
        }
        std::cout << "========================================================" << std::endl;
    }

private:
    std::string title_;                // 报表标题
    std::vector<std::string> headers_; // 表头
    std::unique_ptr<ReportData> data_; // 报表数据
};

// 测试代码
int main() {
    auto start = std::chrono::high_resolution_clock::now();

    // 1. 创建报表原型(加载模板)
    std::vector<std::string> salesHeaders = {"日期", "产品", "销售额", "销量"};
    auto salesProto = std::make_unique<Report>("销售报表", salesHeaders);

    std::vector<std::string> stockHeaders = {"产品", "库存数量", "库存位置", "预警阈值"};
    auto stockProto = std::make_unique<Report>("库存报表", stockHeaders);

    // 2. 克隆报表,填充数据
    // 销售报表数据
    std::vector<std::vector<std::string>> salesData = {
        {"2025-05-20", "手机", "19990", "10"},
        {"2025-05-20", "耳机", "2990", "20"},
        {"2025-05-20", "键盘", "1590", "15"}
    };
    auto salesReport = std::unique_ptr<Report>(dynamic_cast<Report*>(salesProto->clone().release()));
    salesReport->setData(salesData);
    salesReport->generate();

    // 库存报表数据
    std::vector<std::vector<std::string>> stockData = {
        {"手机", "50", "仓库A1区", "10"},
        {"耳机", "30", "仓库A2区", "5"},
        {"键盘", "20", "仓库B1区", "3"}
    };
    auto stockReport = std::unique_ptr<Report>(dynamic_cast<Report*>(stockProto->clone().release()));
    stockReport->setData(stockData);
    stockReport->generate();

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "\n创建2个原型+克隆2个报表总耗时:" << duration.count() << "秒" << std::endl;

    return 0;
}

运行结果

加载销售报表模板(耗时200ms)
加载库存报表模板(耗时200ms)

==================== 销售报表 ====================
          日期           产品         销售额           销量
      2025-05-20           手机         19990           10
      2025-05-20           耳机          2990           20
      2025-05-20           键盘          1590           15
========================================================

==================== 库存报表 ====================
          产品       库存数量       库存位置       预警阈值
           手机            50        仓库A1区           10
           耳机            30        仓库A2区            5
           键盘            20        仓库B1区            3
========================================================

创建2个原型+克隆2个报表总耗时:0.41秒

优势:报表模板只加载一次,克隆报表时只需填充数据,避免重复加载模板,提升报表生成效率,且格式统一,修改模板时只需修改原型。

五、C++ 实战避坑指南:原型模式的 5 个常见误区

原型模式看似简单,但在 C++ 开发中,很多开发者会因忽视细节导致克隆失败、内存泄漏或逻辑错误。下面列出 5 个典型误区及避坑方案。

误区 1:混淆浅克隆和深克隆,导致资源共享问题

问题代码:
class BadPrototype {
public:
    BadPrototype() { data_ = new int[100]; }
    // 浅克隆(默认拷贝构造)
    BadPrototype(const BadPrototype& other) : data_(other.data_) {}
    // 克隆方法返回浅克隆
    std::unique_ptr<BadPrototype> clone() const {
        return std::make_unique<BadPrototype>(*this);
    }
private:
    int* data_; // 动态数组,浅克隆会共享
};
问题分析:

浅克隆导致多个对象共享data_指向的动态数组,一个对象修改数组会影响其他对象,原对象销毁后,克隆对象的data_变成野指针。

避坑方案:
  • 包含动态资源(指针、嵌套对象、容器)的对象,必须使用深克隆;
  • 深克隆时,递归复制所有嵌套资源,确保克隆对象完全独立。

误区 2:克隆方法返回类型错误,导致切片问题

问题代码:
class AbstractProto {
public:
    virtual AbstractProto clone() const = 0; // 返回值为基类对象
};

class ConcreteProto : public AbstractProto {
public:
    AbstractProto clone() const override {
        return ConcreteProto(*this); // 切片:只复制基类部分,丢失子类属性
    }
};
问题分析:

克隆方法返回基类对象,会发生 “对象切片”,克隆后的对象丢失子类特有的成员变量和方法。

避坑方案:
  • 克隆方法返回智能指针(std::unique_ptr<AbstractProto>),避免切片;
  • 客户端通过dynamic_cast将基类指针转换为子类指针,安全访问子类属性。

误区 3:忘记克隆嵌套对象,导致深克隆不彻底

问题代码:
class NestedObj { /* 嵌套对象 */ };

class Proto : public IPrototype {
public:
    std::unique_ptr<IPrototype> clone() const override {
        auto newProto = std::make_unique<Proto>(*this);
        // 忘记克隆nestedObj_,导致浅克隆嵌套对象
        return newProto;
    }
private:
    std::unique_ptr<NestedObj> nestedObj_;
};
问题分析:

深克隆时只复制了外层对象,忘记递归克隆嵌套对象,导致嵌套对象仍然是浅克隆,共享资源。

避坑方案:
  • 深克隆时,遍历所有成员变量,尤其是指针、智能指针、容器,确保每个嵌套资源都被克隆;
  • 给嵌套对象单独实现clone()方法,在外部对象的克隆方法中调用。

误区 4:使用默认拷贝构造替代克隆方法,导致灵活性不足

问题代码:
class Proto {
public:
    // 用拷贝构造替代克隆方法
    Proto copy() const { return *this; }
};

// 客户端
Proto proto;
Proto clone = proto.copy(); // 只能浅拷贝,无法灵活选择浅/深克隆
问题分析:

默认拷贝构造是固定的浅拷贝,无法根据需求切换浅克隆和深克隆,且不支持多态(子类拷贝构造无法覆盖基类)。

避坑方案:
  • 显式声明clone()方法,支持多态克隆;
  • clone()方法中灵活实现浅克隆或深克隆,客户端可根据需求调用。

误区 5:克隆对象时未重置状态,导致状态混乱

问题代码:
class GameRole : public IPrototype {
public:
    GameRole() : hp_(100), isDead_(false) {}
    void attack() { hp_ -= 20; if (hp_ <= 0) isDead_ = true; }

    std::unique_ptr<IPrototype> clone() const override {
        return std::make_unique<GameRole>(*this); // 克隆时复制了isDead_状态
    }
};

// 客户端
auto proto = std::make_unique<GameRole>();
proto->attack(); // 原型hp=80,未死亡
auto clone = proto->clone(); // 克隆对象hp=80,不是初始状态
问题分析:

克隆对象时复制了原型的所有状态(包括临时状态如血量、是否死亡),而客户端可能需要克隆 “初始状态” 的对象。

避坑方案:
  • 克隆对象后,根据需求重置临时状态(如血量、连接状态、计时器);
  • 区分 “配置属性”(如装备、技能)和 “临时状态”(如血量、位置),克隆时只复制配置属性,临时状态初始化。

六、总结:原型模式的核心与实战建议

6.1 核心要点提炼

  1. 核心思想:通过克隆已有原型对象,快速创建新对象,避免重复初始化复杂资源;
  2. 两大克隆方式:浅克隆(快、共享资源)、深克隆(慢、完全独立),根据场景选择;
  3. 3 个核心角色:抽象原型(声明clone())、具体原型(实现克隆逻辑)、客户端(调用clone());
  4. 核心价值:提升复杂对象创建性能、避免代码冗余、支持批量生成相似对象;
  5. 适用场景:复杂对象、对象池、配置克隆、批量生成相似对象。

6.2 C++ 实战建议

  1. 抽象原型设计

    • 用纯虚函数clone()声明克隆接口,返回std::unique_ptr<AbstractPrototype>,避免切片;
    • 抽象原型类必须有虚析构函数,确保子类正确销毁。
  2. 具体原型实现

    • 包含动态资源(指针、嵌套对象)时,必须实现深克隆;
    • 深克隆时,递归复制所有嵌套资源,可给嵌套对象单独实现clone()方法;
    • 克隆后根据需求重置临时状态(如连接状态、血量)。
  3. 客户端使用

    • 先创建原型对象,初始化一次复杂资源;
    • 后续通过clone()快速生成新对象,只修改差异化属性;
    • dynamic_cast将克隆后的基类指针转换为子类指针,安全访问子类方法。
  4. 与其他模式结合

    • 结合单例模式:原型对象设计为单例,确保全局只有一个原型;
    • 结合工厂模式:用工厂管理原型对象,客户端通过工厂获取原型并克隆;
    • 结合对象池模式:对象池存储原型对象,获取对象时克隆,避免竞争。

6.3 最后一句话

原型模式的本质,是 “复用已有对象的资源和配置,高效创建新对象”。它不会让你的代码行数减少,但会让复杂对象的创建从 “耗时费力” 变得 “秒级克隆”,尤其在需要批量生成相似对象的场景中,性能提升极为明显。

记住:原型模式不是 “复制粘贴代码”,而是 “复制对象的资源和状态”。合理使用浅克隆和深克隆,既能提升性能,又能保证代码安全稳定。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值