2025/9/22日 每日总结 设计模式实践:原型模式之向量克隆(深克隆 vs 浅克隆)

设计模式实践:原型模式之向量克隆(深克隆 vs 浅克隆)

在软件开发中,当需要创建多个结构相似的对象时,重复的初始化流程会导致代码冗余且效率低下。原型模式通过"复制现有对象"的方式创建新对象,避免了繁琐的重复构造,尤其适用于复杂对象的创建场景。本文将以数学向量封装为例,用C++实现原型模式,重点解析深克隆与浅克隆的核心差异与实践应用。

一、原型模式核心思想

原型模式是一种创建型设计模式,其核心逻辑是:

  • 定义一个"原型"接口,包含克隆(Clone)方法;
  • 具体对象实现该接口,通过克隆方法返回自身的副本;
  • 客户端无需关注对象创建细节,只需调用克隆方法即可快速生成新对象。
    该模式的优势在于:
  1. 简化对象创建流程,避免重复初始化代码;

  2. 动态扩展对象类型,新增对象只需实现原型接口;

  3. 灵活控制对象复制深度(深克隆/浅克隆),适配不同业务场景。

二、实验场景:向量的封装与克隆

本次实践以数学向量为目标对象,需实现以下需求:

  1. 封装向量的起点(begin)、终点(end)和长度(len),支持动态长度计算;

  2. 用指针实现长度的动态内存分配,支持向量长度灵活修改;

  3. 实现深克隆与浅克隆两种复制方式,并对比其差异。

1. 类图设计

| AbstractVector | // 抽象原型类
+-------------------+


- len: double* // 向量长度指针(动态内存)

- begin: double // 向量起点

- end: double // 向量终点


- Clone(): AbstractVector* // 纯虚克隆方法

- showData(): void // 纯虚数据显示方法
+-------------------+
^
|
+-------------------+
| Vector | // 具体原型类
+-------------------+

- Vector(beg: double, en: double) // 构造函数

- Vector(cp: const Vector&) // 拷贝构造函数(支持深/浅克隆)

- Clone(): AbstractVector* // 实现克隆方法

- showData(): void // 实现数据显示

- ~Vector() // 析构函数
+-------------------+

三、完整代码实现

1. 抽象原型类(AbstractVector)

定义克隆接口和数据显示接口,为具体向量类提供统一规范。

#include <iostream>
using namespace std;
// 抽象原型类:定义克隆接口
class AbstractVector {
public:
// 纯虚克隆方法:子类必须实现,返回自身副本
virtual AbstractVector* Clone() const = 0;
// 纯虚显示方法:展示向量数据
virtual void showData() = 0;
public:
double* len; // 向量长度指针(动态分配内存)
double begin; // 向量起点
double end; // 向量终点
};

2. 具体原型类(Vector)

实现抽象接口,通过拷贝构造函数支持深克隆与浅克隆的动态选择,核心逻辑如下:

  • 构造函数:初始化起点、终点,动态分配长度内存并计算长度;

  • 拷贝构造函数:根据用户选择,决定是共享指针(浅克隆)还是重新分配内存(深克隆);

  • 克隆方法:调用拷贝构造函数创建对象副本;

  • 析构函数:释放动态分配的内存,避免内存泄漏。

    // 具体原型类:实现向量的克隆与数据展示
    class Vector : public AbstractVector {
    public:
    // 构造函数:初始化向量起点、终点,动态计算长度
    Vector(double beg, double en) {
    begin = beg;
    end = en;
    // 动态分配长度内存
    len = new double;
    // 长度为起点和终点的绝对值差
    *len = (end >= begin) ? (end - begin) : (begin - end);
    }
    // 拷贝构造函数:支持深克隆/浅克隆选择
    Vector(const Vector& cp) {
    int choice = 0;
    // 复制基本数据类型(值复制)
    begin = cp.begin;
    end = cp.end;
    // 让用户选择克隆方式
    cout << "请选择克隆方式(深克隆输入1,浅克隆输入2):" << endl;
    cin >> choice;
    if (choice == 1) {
    // 深克隆:为长度重新分配内存,复制值
    len = new double;
    *len = *(cp.len);
    } else {
    // 浅克隆:直接共享原对象的指针地址
    len = cp.len;
    }
    }
    // 实现克隆方法:返回当前对象的副本
    AbstractVector* Clone() const override {
    return new Vector(*this); // 调用拷贝构造函数
    }
    // 实现数据显示方法
    void showData() override {
    cout << "向量长度为:" << *len << endl;
    }
    // 析构函数:释放动态分配的内存
    ~Vector() {
    delete len;
    }
    };
    

    3. 客户端测试代码

    创建原始向量对象,通过克隆方法生成副本,判断克隆类型并展示结果。

    int main() {
    // 1. 创建原始向量对象(起点0,终点10,长度=10)
    AbstractVector* original = new Vector(0, 10);
    cout << "原始向量:";
    original->showData();
    // 2. 克隆向量对象
    AbstractVector* cloned = original->Clone();
    cout << "克隆向量:";
    cloned->showData();
    // 3. 判断克隆类型(通过比较长度指针地址)
    if (original->len == cloned->len) {
    cout << "\n克隆类型:浅克隆" << endl;
    cout << "原向量len地址:" << original->len << endl;
    cout << "克隆向量len地址:" << cloned->len << endl;
    cout << "结论:浅克隆共享指针地址,修改一个会影响另一个" << endl;
    } else {
    cout << "\n克隆类型:深克隆" << endl;
    cout << "原向量len地址:" << original->len << endl;
    cout << "克隆向量len地址:" << cloned->len << endl;
    cout << "结论:深克隆独立分配内存,对象完全独立" << endl;
    }
    // 释放内存
    delete original;
    delete cloned;
    system("pause");
    return 0;
    }
    

    四、运行结果与分析

    1. 浅克隆运行结果

    原始向量:向量长度为:10
    请选择克隆方式(深克隆输入1,浅克隆输入2):
    2
    克隆向量:向量长度为:10
    克隆类型:浅克隆
    原向量len地址:0000021F7D7C24E0
    克隆向量len地址:0000021F7D7C24E0
    结论:浅克隆共享指针地址,修改一个会影响另一个
    

    2. 深克隆运行结果

    原始向量:向量长度为:10
    请选择克隆方式(深克隆输入1,浅克隆输入2):
    1
    克隆向量:向量长度为:10
    克隆类型:深克隆
    原向量len地址:0000021F7D7C2520
    克隆向量len地址:0000021F7D7C2560
    结论:深克隆独立分配内存,对象完全独立
    

    3. 核心差异验证

    若在浅克隆后修改原向量的长度:

    // 浅克隆场景下添加以下代码
    *original->len = 20; // 修改原向量长度
    cout << "修改后原向量:";
    original->showData(); // 输出20
    cout << "修改后克隆向量:";
    cloned->showData(); // 输出20(受影响)
    

    深克隆场景下修改原向量长度,克隆向量不受影响,因为两者的len指针指向不同内存空间。

    五、深克隆与浅克隆详细对比

    特性 浅克隆(Shallow Clone) 深克隆(Deep Clone)
    实现逻辑 仅复制对象的基本数据类型,引用类型(指针、引用)共享地址 复制基本数据类型,同时为引用类型重新分配内存并复制内容
    数据独立性 低:修改原对象的引用数据会影响克隆对象 高:原对象与克隆对象完全独立,互不影响
    内存开销 小:无需额外分配内存 大:需要为引用类型分配新内存
    实现复杂度 简单:直接赋值即可 复杂:需递归处理多层引用类型(如嵌套指针)
    内存管理风险 高:容易出现重复释放(double free)问题 低:各自管理独立内存,析构安全
    适用场景 1. 对象无引用类型成员;2. 引用数据只读;3. 追求创建效率 1. 对象包含动态分配的引用类型;2. 需要对象完全独立;3. 避免副作用

    六、原型模式的应用场景

  1. 复杂对象创建:当对象初始化流程繁琐(如需要调用多个接口、查询数据库),克隆现有对象比重新创建更高效;

  2. 动态扩展对象类型:无需新增子类,通过克隆不同原型对象即可生成不同类型的实例;

  3. 避免构造函数耦合:客户端无需依赖具体构造函数参数,只需通过克隆接口创建对象,降低耦合度;

  4. 批量创建相似对象:如游戏中的敌人、粒子系统中的粒子,通过克隆原型对象快速生成大量相似实例。

七、实践总结

  1. 原型模式的核心是"克隆接口",通过Clone方法将对象创建逻辑封装在自身,客户端无需关注构造细节;

  2. 浅克隆与深克隆的关键区别在于引用类型的处理方式:浅克隆共享地址,深克隆独立分配内存;

  3. C++中实现原型模式需注意:

  • 抽象原型类需定义纯虚Clone方法;
  • 具体类通过拷贝构造函数实现克隆逻辑;
  • 深克隆需手动管理动态内存,避免内存泄漏;
  • 浅克隆需谨慎处理析构函数,防止重复释放。
    通过本次向量克隆的实践,我对原型模式的设计思想和深/浅克隆的本质差异有了更清晰的认识。在实际开发中,需根据对象的成员结构(是否包含动态引用类型)选择合适的克隆方式,平衡效率与数据独立性。
posted @ 2025-12-29 14:29  Moonbeamsc  阅读(5)  评论(0)    收藏  举报
返回顶端