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矩阵元素设置初始化打印矩阵销毁…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...