c++设计模式二:原型模式
使用场景:当需要构建多个相同的类对象时,而且该类对象结构较为复杂,如果每个都重新组织构建会很麻烦。
其实,就是写一个拷贝构造函数,或者写一个拷贝每个成员变量的clone()方法。
举例说明:比如一个相亲网站需要定义一个“男人”类,可想而知描述清楚一个男人肯定需要很多属性吧,比如姓名、身高、体重、职业、财产大致情况、联系电话、微信号、QQ号等。如果一个男人同时有多个相亲对象时,每次都重新构建填写属性显然太麻烦,所以类中需要定义一个拷贝的方法。
先写一个简单的类:
#pragma once
#include <QString>
class singleMan
{
public:singleMan(QString name,double height,double weight,QString career,QString phoneNum);~singleMan();QString m_name;//姓名double m_height;//身高double m_weight;//体重QString m_career;//职业QString m_phoneNum;//电话};
#include "singleMan.h"
singleMan::singleMan(QString name, double height, double weight, QString career, QString phoneNum):m_name(name),m_height(height),m_weight(weight),m_career(career),m_phoneNum(phoneNum)
{
}singleMan::~singleMan()
{
}
直接赋值,打印出的内容也完全一样,还需要写什么拷贝构造吗?
singleMan sman("LaoWang", 178, 153, "programmer", "19969961024");singleMan sman1 = sman;cout << "sman:" << sman.m_name.toStdString() << ";" << sman.m_phoneNum.toStdString() << endl;cout << "sman1:" << sman1.m_name.toStdString() << ";" << sman1.m_phoneNum.toStdString() << endl;

这里c++会自动创建一个默认的拷贝构造函数,将成员变量中的值复制一次。但是,当类中有指针类型成员变量时,默认的构造函数仅仅把地址复制一份,两个对象指向的是同一个内存地址,对象销毁时会造成同一个内存被释放两次造成double free,例子如下,添加一个指针类型成员变量-近半年工资流水:
singleman类中添加double* m_monthSalary指针成员变量:
double* m_monthSalary = nullptr;//近半年工资流水
类的析构中释放指针空间:
singleMan::~singleMan()
{if (m_monthSalary!=nullptr){delete m_monthSalary;m_monthSalary = nullptr;}
}
double salary[6] = { 30000,30000,40000,50000,60000 };sman.m_monthSalary = salary;singleMan sman2 = sman;cout << "sman:" << sman.m_monthSalary[0] << sman.m_monthSalary[1] << sman.m_monthSalary[2] << sman.m_monthSalary[3] << endl;cout << "sman2:" << sman2.m_monthSalary[0] << sman2.m_monthSalary[1] << sman2.m_monthSalary[2] << sman2.m_monthSalary[3] << endl;

调用打印没问题,但是程序结束后会报错:

所以,这里需要自己写一个拷贝构造函数,实现中需要重新分配新的内存地址(即所谓的深拷贝):
//拷贝构造singleMan(const singleMan& man);//这里要用引用,引用做参数时可减少一次对象拷贝,同时避免了循环拷贝构造问题;
singleMan::singleMan(const singleMan& man)
{m_career = man.m_career;m_height = man.m_height;m_name = man.m_name;m_phoneNum = man.m_phoneNum;m_weight = man.m_weight;m_monthSalary = new double[6];memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));
}
double* salary = new double[6];salary[0] = 30000;salary[1] = 30000;salary[2] = 40000;salary[3] = 50000;salary[4] = 60000;salary[5] = 65000;sman.m_monthSalary = salary;singleMan sman2 = sman;cout << "sman salary: " << sman.m_monthSalary << endl;cout << "sman2 salary: " << sman2.m_monthSalary << endl;cout << "sman: " << sman.m_monthSalary[0]<<"," << sman.m_monthSalary[1] << "," << sman.m_monthSalary[2] << "," << sman.m_monthSalary[3] << endl;cout << "sman2:" << sman2.m_monthSalary[0] << "," << sman2.m_monthSalary[1] << "," << sman2.m_monthSalary[2] << "," << sman2.m_monthSalary[3] << endl;

此时,上述问题貌似已经解决了,但是在实际使用过程中会有这种用法:将一个类对象赋值给另一个已经构造好的对象:
singleMan sman3("LaoLi", 183, 160, "programmer", "19969960512");sman3 = sman;cout << "sman: " << sman.m_name.toStdString() << ";" << sman.m_phoneNum.toStdString() << endl;cout << "sman3:" << sman3.m_name.toStdString() << ";" << sman3.m_phoneNum.toStdString() << endl;cout << "sman salary: " << sman.m_monthSalary << endl;cout << "sman3 salary: " << sman3.m_monthSalary << endl;cout << "sman: " << sman.m_monthSalary[0] << "," << sman.m_monthSalary[1] << "," << sman.m_monthSalary[2] << "," << sman.m_monthSalary[3] << endl;cout << "sman3:" << sman3.m_monthSalary[0] << "," << sman3.m_monthSalary[1] << "," << sman3.m_monthSalary[2] << "," << sman3.m_monthSalary[3] << endl;

这时,就不进入拷贝构造函数了,而是进入了默认的赋值操作符函数中,会有同样的问题,这时需要写一个赋值操作符重载函数:
//赋值运算符重载(用新的对象的值全部替换原有项)singleMan& operator=(const singleMan& man);
singleMan& singleMan::operator=(const singleMan& man)
{if (m_monthSalary != nullptr){delete m_monthSalary;m_monthSalary = nullptr;}m_career = man.m_career;m_height = man.m_height;m_name = man.m_name;m_phoneNum = man.m_phoneNum;m_weight = man.m_weight;m_monthSalary = new double[6];memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));return *this;
}
再次调用问题得到解决:

最后,由于拷贝构造函数中代码和赋值运算符代码几乎一致,可以在拷贝构造函数中直接调研赋值运算符重载函数:
singleMan::singleMan(const singleMan& man)
{/*m_career = man.m_career;m_height = man.m_height;m_name = man.m_name;m_phoneNum = man.m_phoneNum;m_weight = man.m_weight;m_monthSalary = new double[6];memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));*/*this = man;
}
如有问题,欢迎指正!
参考文献:
【精选】C++创建型模式-原型模式_c++ 原型模式-CSDN博客
4. 原型模式(Prototype) (yuque.com)
相关文章:
c++设计模式二:原型模式
使用场景:当需要构建多个相同的类对象时,而且该类对象结构较为复杂,如果每个都重新组织构建会很麻烦。 其实,就是写一个拷贝构造函数,或者写一个拷贝每个成员变量的clone()方法。 举例说明:比如一个相亲网站…...
【Qt控件之QMessageBox】详解
Qt控件之QMessageBox 描述基于属性的API富文本和文本格式属性严重程度以及图标和Pixmap属性静态函数API 高级用法默认按钮和退出按钮示例使用场景 描述 QMessageBox类提供了一个模态对话框,用于通知用户或向用户提问并接收答案。 消息框显示一个主要文本以提醒用户…...
SSH安全登录远程主机
SSH服务器简介 SSH即Security SHell的意思,它可以将连线的封包进行加密技术,之后进行传输,因此相当的安全。 SSH是一种协议标准,其目的是实现安全远程登录以及其它安全网络服务。 SSH协定,在预设的状态下,…...
揭秘!产品经理提升效率的秘密武器:10款AI生成PPT工具
AI的爆炸式增长表现令人惊艳,现有的各类AI工具正在重塑各行各业,不同程度地提高人们的工作效率,并有望创造新的职业机会。但是,面对市面上数量众多的AI工具,且每周都会蹦出新的产品,即便是以好奇心富称的产…...
Oracle修改带数据的字段类型
insert into TNW_FUND_SELORG(TFDINFOID,TSOINFOID) select TFD_INFO_ID,TSO_INFO_ID from TFD_SEL_FUNDLINK_TO_OLDFUNDWEB_DB /*修改原字段名*/ ALTER TABLE 表名 RENAME COLUMN 字段名 TO 字段名1; /*添加一个和原字段同名的字段*/ ALTER TABLE 表名 ADD 字段名 VARCHAR…...
WebService接口方式和Restful接口这两者有什么区别和相同点
WebService和RESTful接口都是用于在网络上进行通信和数据交换的技术,但它们在设计和使用上有一些重要的区别和相似之处。 相同点: 基于HTTP协议:无论是WebService还是RESTful接口,它们都是通过HTTP协议进行通信的。 支持多种数据…...
jenkins自动化操作步骤(gitblit)
1、登陆地址: http://xxxxxxxxx.org:xxxx/ admin/xxxx 2、创建任务 选择构建一个maven项目 3、配置 最多只保留一天一个任务 选择git仓库和账号密码 选择代码对应分支 build项: 1)使用父项目的pom文件:k56-boot/pom.xml 2&…...
centos中mongodb设置服务自启动并 允许远程IP访问
安装mongodb参考 注意的是配置文件需要把journal设置为true 制作为系统服务 创建MongoDB服务文件。运行以下命令创建服务文件/etc/systemd/system/mongod.service: vi /etc/systemd/system/mongod.service [Unit] DescriptionMongoDB Database Server Documenta…...
实时定位和配送追踪:开发万岳同城外卖APP的关键技术特性
随着生活节奏的不断加快,外卖服务已经成为许多人日常生活中不可或缺的一部分。无论是工作日的午餐,还是周末的家庭聚会,外卖APP已经成为满足各种美食需求的首选方式。然而,同城外卖APP的成功不仅仅取决于美味的食物选择࿰…...
数据库强化(3.存储过程)
1.什么是存储过程? 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。MySQL 5.0 版本开始支持存储过程。 它是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中&a…...
雅思小作文笔记
mostly from Simon’s methods and techniques remember the task is describe what you see, not give an opinion. Just write a report.no conclusion, just a summary(the overview) Question type 小作文的题目类型大致如上 Simon所述,在描述数字的时候&…...
Java List Set Map
一、List 1.1 ArrayList 1.2 LinkedList 二、Set 2.1 HashSet 2.2 TreeSet 2.3 LinkedHashSet 三、Map 3.1 HashMap 3.2 TreeMap 3.3 LinkedHashMap 四、对比 类型底层结构重复null值场景备注查询删除新增ListArrayList动态数组可允许快速随机访问元素0(1)0(n)尾部增加0&a…...
【数据结构】数组和字符串(十三):链式字符串的基本操作(串长统计、查找、复制、插入、删除、串拼接)
文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作(链式存储)1. 结构体2. 初始化3. 判空4. 串尾添加5. 打印6. 串长统计7. 查找8. 复制9. 插入10. 删除11. 串拼接12. 销毁13. 主函数14. 代码整合 4.3 字符串 字符串(String)是由零个或…...
Python3 获取当前服务器公网 IP 地址
有同学问我如何使用 Python 获取服务器公网的 IP 地址呢?我测试几个发现,方法有很多,好用的就发现一种,即直接使用 Python 自带的 socket 包。 代码示例: # 获取主机 IP dgram socket.socket(socket.AF_INET, socke…...
EAS查前5分钟到现在的组织变动数据
select * from T_ORG_Admin where ( (FCREATETIME between ( sysdate-(1/24/60)*8) and sysdate ) or (FLASTUPDATETIME between ( sysdate-(1/24/60)*8) and sysdate ) ) -- FLASTUPDATETIME < sysdate-(1/24/60)*10 --FNUMBER 110112...
uni-app——如何阻止事件冒泡
引言 在开发移动应用程序时,我们经常需要处理用户交互事件。然而,有时候这些事件会冒泡,导致意外的行为和不良用户体验。在本文中,我们将探讨如何使用UniApp框架来阻止事件冒泡,并提供一些示例代码来帮助您理解如何实…...
[MySQL]索引
目录 概念解释 作用/优点 缺点 适用场景 索引的创建,删除与查看 系统对索引的自动创建 索引建立的时机 索引存储的数据结构 选择B树的原因 B树的原理 查询流程 优点 B树 与B树的区别 优点 概念解释 索引就像是一本字典的目录,我们可以根据目录快速定位到我们想…...
什么是AUTOSAR ComStack,AUTOSAR架构中,CAN通信堆栈CAN Communication Stack介绍
AUTOSAR(Automotive Open System Architecture)ComStack指的是AUTOSAR架构中的通信堆栈。在AUTOSAR体系结构中,ComStack是指用于不同软件组件(如应用软件、基础软件等)之间进行通信的一组协议和服务。 在AUTOSAR架构中…...
黄金期货与黄金现货的区别
黄金期货与黄金现货是有区别的,比如在交易机制方面,黄金期货有具体的交割日,合约到期就必须交割,否则会被强行平仓或以实物进行交割,而在保证金不足时也会被强行平仓;而现货黄金就没有交割限制,…...
【数据结构】数组和字符串(五):特殊矩阵的压缩存储:稀疏矩阵——压缩稀疏行(CSR)
文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表e. 压缩稀疏行(Compressed Sparse Row,CSR)矩阵结构体创建CSR矩阵元素设置初始化打印矩阵销毁…...
2026年公司文化专题片拍摄公司排行榜:行业深度解析
引言随着企业对品牌传播和文化建设的重视程度不断提升,公司文化专题片成为展示企业形象、传递核心价值观的重要手段。越来越多的企业开始关注如何通过高质量的专题片来提升品牌形象和企业文化影响力。本文将深入分析2026年公司文化专题片拍摄行业的趋势,…...
企业智能体如何高效快速部署落地,这N个细节需要注意
随着企业级智能体技术的日趋成熟,越来越多企业将其作为数字化转型的重要抓手,期望通过智能体提升业务效率、降低运营成本。但现实中,多数企业陷入“部署慢、落地难、效果差”的困境:有的耗时数月仍无法正常上线,有的上…...
Redis分布式锁进阶第六十八篇
一、本篇前置衔接 第六十八篇我们完成了全系列终局复盘,整理了故障排查SOP与企业级落地铁律。常规单资源锁、热点分片锁、隔离锁全部讲透,但真实复杂业务永远不是单一资源:下单要扣库存、扣优惠券、扣积分、冻结余额,多资源并行争…...
CircuitPython微控制器图形保存实战:从屏幕截图到BMP文件生成
1. 项目概述:为什么我们需要在微控制器上保存图形? 在嵌入式开发领域,尤其是当我们使用像Adafruit PyPortal、PyGamer这类带有彩色显示屏的开发板时,图形界面的调试和内容存档一直是个不大不小的痛点。想象一下,你花了…...
华为HCSP认证备考全攻略:5G网优方向
华为HCSP(Huawei Certified Service Professional)认证是5G网优行业的重要资质认证。本文从考试内容、备考策略、真题分析三个维度,帮你一次性通过考试。一、HCSP认证体系概览1.1 认证等级等级全称定位考试难度薪资加成HCIAHuawei Certified …...
Go语言秘钥管理:K8s Secret
Go语言秘钥管理:K8s Secret 1. Secret使用 import ("k8s.io/client-go/kubernetes""k8s.io/client-go/rest" )func getSecret(clientset *kubernetes.Clientset, name, namespace string) (string, error) {secret, err : clientset.CoreV1()…...
JDK 17文本块实战:告别繁琐拼接,拥抱多行字符串新写法
1. 为什么我们需要文本块? 如果你写过Java代码,肯定遇到过这样的场景:需要处理多行字符串,比如HTML模板、SQL语句或者JSON数据。在JDK 17之前,我们只能通过字符串拼接的方式来实现,代码看起来就像是一团乱麻…...
图记忆架构:用知识图谱增强AI智能体的长期记忆与推理能力
1. 项目概述:当记忆成为可编程的图最近在探索如何让AI应用真正“记住”复杂的上下文时,我遇到了一个非常有意思的项目:openclaw-memory-graphiti。这个名字听起来有点拗口,但拆解一下就能明白它的野心——“OpenClaw”可能是一个开…...
小学期学习报告-1
通过B站视频学习之后,我掌握冰设计出了555方波发生电路和低通滤波器,通过示波器可以看到,已经除了稳定的方波和正弦波 在这个过程中,根据公式T0.7*( R12R2)*C1,多次调整并得出稳定波形ÿ…...
U-Boot MMC DM驱动移植实战:从设备树配置到调试排错
1. 项目概述与核心价值最近在为一个基于i.MX6UL的工控板卡适配新的eMMC存储芯片时,又和U-Boot的MMC驱动打了一次交道。这让我想起,很多嵌入式开发者在进行板级移植或更换存储介质时,面对U-Boot中那套基于设备模型(Device Model, D…...
