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矩阵元素设置初始化打印矩阵销毁…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
