【第五节】C++设计模式(创建型模式)-Prototype(原型)模式

news/2025/2/26 1:18:54

目录

一、问题背景

二、 模式选择

三、讨论总结


一、问题背景

        在软件开发中,有时我们需要通过已有对象来创建新对象,而不是从头开始构建。这种需求让我想起了现代制造业中的 3D 打印技术。通过扫描一个现有的物体,3D 打印机可以快速复制出多个完全相同的副本,而无需重新设计和建模。这种“复制已有对象”的能力在编程中也有对应的设计模式,那就是 Prototype 模式

        C++中通过拷贝构造函数实现对象复制,但这一机制存在两个关键挑战:

                浅拷贝陷阱:默认拷贝构造的按位复制可能引发指针悬挂问题

                多态复制难题:基类指针无法直接调用派生类的拷贝构造函数

        这些问题既是面试常见考点,也是系统稳定性的潜在威胁。原型模式通过标准化的复制接口,为这些问题提供了系统化的解决方案。

二、 模式选择

        Prototype 模式的结构非常简单,其核心是一个 `Clone` 接口,用于复制对象。在 C++ 中,`Clone` 方法通常通过拷贝构造函数来实现。以下是 Prototype 模式的典型结构图:

原型模式的核心是Clone抽象接口,其标准实现包含三个关键组件:

        Prototype:定义克隆接口的抽象基类

        ConcretePrototype:实现具体克隆操作的可复制对象

        Client:通过原型对象创建新实例的使用者

实现
        为了帮助初学者更好地理解 Prototype 模式,下面将提供一个完整的 C++ 实现示例。代码分为三个部分:`Prototype.h`、`Prototype.cpp` 和 `main.cpp`。

代码片段 1:Prototype.h

// Prototype.h
#ifndef _PROTOTYPE_H_
#define _PROTOTYPE_H_

// 抽象基类 Prototype
class Prototype {
public:
    virtual ~Prototype() = default; // 虚析构函数,确保派生类对象正确释放资源
    virtual Prototype* Clone() const = 0; // 纯虚函数,用于复制对象
protected:
    Prototype() = default; // 保护构造函数,防止直接实例化
};

// 具体实现类 ConcretePrototype
class ConcretePrototype : public Prototype {
public:
    ConcretePrototype() = default; // 默认构造函数
    ConcretePrototype(const ConcretePrototype& cp); // 拷贝构造函数
    ~ConcretePrototype() override = default; // 析构函数
    Prototype* Clone() const override; // 实现 Clone 方法
};

#endif // ~_PROTOTYPE_H_

代码片段 2:Prototype.cpp

// Prototype.cpp
#include "Prototype.h"
#include <iostream>
using namespace std;

// Prototype 类的实现
Prototype::Prototype() {
    // 基类构造函数,用于初始化基类成员(如果有)
}

Prototype::~Prototype() {
    // 基类析构函数,用于释放基类资源(如果有)
}

Prototype* Prototype::Clone() const {
    return nullptr; // 基类中返回空指针,表示不支持直接复制
}

// ConcretePrototype 类的实现
ConcretePrototype::ConcretePrototype() {
    // 默认构造函数,用于初始化具体类成员(如果有)
}

ConcretePrototype::~ConcretePrototype() {
    // 析构函数,用于释放具体类资源(如果有)
}

// 拷贝构造函数
ConcretePrototype::ConcretePrototype(const ConcretePrototype& cp) {
    cout << "ConcretePrototype copy ..." << endl; // 打印拷贝构造信息
    // 如果需要深拷贝,可以在这里实现
}

// 实现 Clone 方法
Prototype* ConcretePrototype::Clone() const {
    return new ConcretePrototype(*this); // 调用拷贝构造函数,返回新对象
}

代码片段 3:main.cpp

// main.cpp
#include "Prototype.h"
#include <iostream>
using namespace std;

int main(int argc, char* argv[]) {
    // 创建原型对象
    Prototype* p = new ConcretePrototype();

    // 复制对象
    Prototype* p1 = p->Clone();

    // 释放资源
    delete p;
    delete p1;

    return 0;
}

代码说明

实现要点

        深拷贝控制:在拷贝构造函数中实现资源复制逻辑

        多态支持:基类声明虚析构函数确保正确释放资源

        接口隔离:保护构造函数强制使用克隆方式创建对象

        Prototype 模式的结构和实现都非常简单,其核心在于 `Clone` 方法的实现。在 C++ 中,`Clone` 方法通常通过拷贝构造函数来完成对象的复制。为了简化示例,这里没有涉及深拷贝(即对象中包含指针或复合对象的情况),而是直接使用了编译器提供的默认拷贝构造函数(按位拷贝)。

        需要注意的是,这种实现方式仅适用于简单场景。如果对象中包含动态分配的资源(如指针),则需要手动实现深拷贝,以避免潜在的内存问题。
 

三、讨论总结

        Prototype 模式通过复制原型对象来创建新对象,这种方式特别适用于以下场景:
(1)当对象的创建过程比较复杂,且需要频繁创建相似对象时。
(2)当需要避免重复初始化对象的开销时。

        与其他创建型模式(如 Builder 模式和 AbstractFactory 模式)相比,Prototype 模式的独特之处在于:
        Builder 模式:侧重于逐步构建复杂对象,而不是直接返回对象。
        AbstractFactory 模式:用于创建一系列相互依赖的对象。
        Prototype 模式:通过复制自身来创建新对象。

        Prototype 模式的核心思想是将对象创建的责任交给对象本身,而不是外部工厂。这种设计不仅提高了代码的灵活性,还减少了重复代码的编写。


        Prototype 模式是一种简单而强大的设计模式,它通过复制现有对象来创建新对象,避免了重复初始化的开销。在 C++ 中,拷贝构造函数是实现 Prototype 模式的关键,但需要注意浅拷贝和深拷贝的问题。通过合理使用 Prototype 模式,可以显著提高代码的复用性和可维护性。

由此原型模式通过标准化对象复制接口,有效解决了以下问题:

        构造开销:避免重复初始化操作

        多态复制:支持通过基类接口复制派生类对象

        状态克隆:完整复制对象的瞬时状态

在C++实现中需特别注意:

        严格实现深拷贝防止内存问题

        通过虚析构函数保证多态正确性

        考虑使用智能指针管理克隆对象生命周期

        该模式在游戏开发(角色克隆)、文档编辑(版本快照)等领域有广泛应用,是构建高效对象创建系统的利器。


http://www.niftyadmin.cn/n/5867023.html

相关文章

SpringSecurity基于注解实现方法级别授权:@PreAuthorize、@PostAuthorize、@Secured

Spring Security 访问权限系列文章: 《SpringSecurity基于配置方法控制访问权限:MVC匹配器、Ant匹配器》 《SpringSecurity基于注解实现方法级别授权:@PreAuthorize、@PostAuthorize、@Secured》 《SpringSecurity设置白名单》 方法调用授权的含义很明确,与 HTTP 端点级别的…

【GESP】C++二级模拟 luogu-b3995, [GESP 二级模拟] 小洛的田字矩阵

GESP二级模拟题&#xff0c;多层循环、分支语句练习&#xff0c;难度★✮☆☆☆。 题目题解详见&#xff1a;https://www.coderli.com/gesp-2-luogu-b3995/ 【GESP】C二级模拟 luogu-b3995, [GESP 二级模拟] 小洛的田字矩阵 | OneCoderGESP二级模拟题&#xff0c;多层循环、分…

Codes 开源免费研发项目管理平台 2025年第一个大版本3.0.0 版本发布及创新的轻IPD实现

Codes 简介 Codes 是国内首款重新定义 SaaS 模式的开源项目管理平台&#xff0c;支持云端认证、本地部署、全部功能开放&#xff0c;并且对 30 人以下团队免费。它通过创新的方式简化研发协同工作&#xff0c;使敏捷开发更易于实施。并提供低成本的敏捷开发解决方案&#xff0…

【RK3588嵌入式图形编程】-SDL2-构建交互式按钮

构建交互式按钮 文章目录 构建交互式按钮1、回顾UI组件2、创建按钮类3、鼠标悬停事件4、处理点击事件5、创建派生按钮6、扩展功能6.1 禁用和启用按钮6.2 鼠标悬停反应7、总结在本文中,将探索构建响应用户输入的UI组件的技术。 在本文中,我们将学习如何使用SDL在C++中构建交互…

大语言模型中的 Token如何理解?

在大语言模型中&#xff0c;Token 是文本处理的基本单元&#xff0c;类似于“文字块”&#xff0c;模型通过将文本分割成Token来理解和生成内容。举一个形象一点的例子&#xff0c;可以理解为 AI 处理文字时的“最小积木块”。就像搭乐高时&#xff0c;每块积木是基础单位一样&…

el-select滚动获取下拉数据;el-select滚动加载

el-select下拉获取数据 1.解决问题2.封装MyScrollSelect组件3.使用MyScrollSelect组件 1.解决问题 场景&#xff1a;下拉数据量过大&#xff0c;后端提供一个分页查询接口&#xff1b;需要每次滚动加载下一页的下拉数据 且单选的状态&#xff0c;需要支持回显&#xff0c;通过n…

unity学习52:UI的最基础组件 rect transform,锚点anchor,支点/轴心点 pivot

目录 1 image 图像&#xff1a;最简单的UI 1.1 图像的基本属性 1.2 rect transform 1.3 image的component: 精灵 → 图片 1.4 修改颜色color 1.5 修改材质 1.6 raycast target 1.7 maskable 可遮罩 1.8 imageType 1.9 native size 原生大小 2 rect transform 2.1 …

Ajax基础详解

AJAX简介&#xff1a; AJAX是一种前后端交互的技术&#xff0c;通过AJAX能够使用js向服务端发送携带或不携带信息的请求&#xff0c;返回的响应体中会有服务端根据发送的请求报文处理后的结果&#xff0c;这些结果在在前端使用。 AJAX全名&#xff1a; async javascript and X…