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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/108249.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Megatron-LM GPT 源码分析(四) Virtual Pipeline Parallel分析

引言 本文接着上一篇【Megatron-LM GPT 源码分析&#xff08;三&#xff09; Pipeline Parallel分析】&#xff0c;基于开源代码 GitHub - NVIDIA/Megatron-LM: Ongoing research training transformer models at scale &#xff0c;通过GPT的模型运行示例&#xff0c;从三个维…

51单片机复位电容计算与分析(附带Proteus电路图)

因为iC x (dU/dt).在上电瞬间&#xff0c;U从0变化到U,所以这一瞬间就是通的&#xff0c;然后这就是一个直流回路&#xff0c;因为电容C直流中是断路的&#xff0c;所以就不通了。 然后来分析一下这个电容的电压到底是能不能达到单片机需要的复位电压。 这是一个线性电容&…

Django 全局配置 settings 详解

文章目录 1 概述1.1 Django 目录结构 2 常用配置&#xff1a;settings.py2.1 注册 APP&#xff1a;INSTALLED_APPS2.2 模板路径&#xff1a;TEMPLATES2.3 静态文件&#xff1a;STATICFILES_DIRS2.4 数据库&#xff1a;DATABASES2.5 允许访问的主机&#xff1a;ALLOWED_HOSTS 1 …

算法通过村第十七关-贪心|黄金笔记|跳跃游戏

文章目录 前言跳跃游戏最短跳跃游戏总结 前言 提示&#xff1a;曾走过山&#xff0c;走过水&#xff0c;其实只是借助他们走过我的生命&#xff1b;我看着天&#xff0c;看着地&#xff0c;其实只是借助它们确定我的位置&#xff1b;我爱这她&#xff0c;爱着你&#xff0c;其实…

RabbitMQ (4)

RabbitMQ (4) 文章目录 1. 死信的概念2. 死信的来源3. 死信代码案例3.1 TTL 过期时间3.2 超过队列最大长度3.3 拒绝消息 前言   上文我们已经学习完 交换机 &#xff0c;知道了几个交换机的使用 &#xff0c;下面我们来学习一下 死信队列 1. 死信的概念 先从概念解释上搞清楚这…

BUUCTF刷题记录

[BJDCTF2020]Easy MD51 进入题目页面&#xff0c;题目提示有一个链接&#xff0c;应该是题目源码 进入环境&#xff0c;是一个查询框&#xff0c;无论输入什么都没有回显&#xff0c;查看源码也没什么用 利用bp抓包查看有没有什么有用的东西 发现响应的Hint那里有一个sql语句&…

WIN11新版画图问题解决

1 白色背景被连同删除的问题 解决方法&#xff1a;加层 将层调整为新建的层&#xff0c;在这个层下画图就行。 2 QQ截图无法直接放在画图上的问题 使用QQ截图的时候&#xff1a; 解决方法&#xff1a;使用windows自带的截图工具 步骤&#xff1a; 1. 使用快捷键winshifts 2.…

【Git企业开发】第一节.Git 初识

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a; 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff01;&#xff01…

Remote Local File Inclusion (RFI/LFI)-文件包含漏洞

在Web应用开发过程中,程序开发者经常会把具有某一功能的部分代码封装起来形成独立的文件,在后续想实现该功能时,就不需要重复编写,直接调用文件,大大提高编程效率。这种调用文件的过程一般被称为文件包含。开发人员为了使代码更灵活,会将被包含的文件设置为变量,用来进行…

1.1 计算机安全概念

思维导图&#xff1a; 前言&#xff1a; 第1章: 计算机与网络安全概念笔记 1. 学习目标 了解保密性、完整性和可用性的关键安全需求。了解OSI的X.800安全架构。识别和举例说明不同的安全威胁和攻击。掌握安全设计的基本准则。熟悉攻击面和攻击树的使用。了解与密码标准相关的…

EASYX剪切区域

eg1:EASY中的颜色模型 可以参考推荐16进制颜色表&#xff1a;https://www.codeeeee.com/color/rgb.html 参考学习EASYX在线文档https://docs.easyx.cn/zh-cn/drawing-func easyx的基本概念和使用方式 #include <stdio.h> #include <easyx.h> #include <iostr…

Winform 多语言化快速解析替换工具-1分钟一个界面

随着业务的扩展&#xff0c;有的软件有多语言化的需求。那么如果软件已经很多写死的文字内容如何快速进行语言化替换呢&#xff0c;一个一个去改工作量太大。 于是开发了个小工具用来替换现有内容并生成语音包&#xff0c;原理就是采用正则表达式进行匹配控件关键字以及中文进…

orb-slam3编译手册(Ubuntu20.04)

orb-slam3编译手册&#xff08;Ubuntu20.04&#xff09; 一、环境要求1.安装git2.安装g3.安装CMake4.安装vi编辑器 二、源代码下载三、依赖库下载1.Eigen安装2.Pangolin安装3.opencv安装4.安装Python & libssl-dev5.安装boost库 三、安装orb-slam3四、数据集下载及测试 写在…

关于线性模型的底层逻辑解读 (机器学习 细读01)

一 多元线性回归 线性回归是机器学习中 有监督机器学习 下的一种算法。 回归问题主要关注的是因变量(需要预测的值&#xff0c;可以是一个也可以是多个)和一个或多个数值型的自变量(预测变量)之间的关系。 需要预测的值:即目标变量&#xff0c;target&#xff0c;y&#xff0c…

计算机网络重点概念整理-第六章 应用层【期末复习|考研复习】

第六章 应用层 【期末复习|考研复习】 计算机网络系列文章传送门&#xff1a; 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 第六章 应用层 【期末复习|考研复习…

爬取抖音用户的个人基本信息

今年夏季&#xff0c;大概七八月份&#xff0c;刀郎开通抖音账号&#xff0c;并在抖音上发布多首作品&#xff0c;一时之间其热度暴涨&#xff0c;其粉丝也是与日俱增。 有人为了蹭热度&#xff0c;直播刀郎粉丝的实时变化情况&#xff0c;直播热度最高的时候同时几千人在线观…

HttpClient远程使用大全

一 HttpClient简介 1.1 概述 HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。主要实现功能&#xff1a; 实现了所有 HTTP 的方法&#xff08;GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等&#xff09; 支持 HTTPS 协议 支持代理服务器&#xff08;Nginx…

AS/400简介

AS400 AS400 简介AS/400操作系统演示 AS400 简介 在 AS400 中&#xff0c;AS代表“应用系统”。它是多用户、多任务和非常安全的系统&#xff0c;因此用于需要同时存储和处理敏感数据的行业。它最适合中级行业&#xff0c;因此用于制药行业、银行、商场、医院管理、制造业、分销…

Web APIs——事件流

一、事件流 1.1 事件流与两个阶段说明 事件流指的是事件完整执行过程中的流动路径 说明&#xff1a;假设页面里有个div&#xff0c;当触发事件时&#xff0c;会经历两个阶段&#xff0c;分别是捕获阶段、冒泡阶段 简单来说&#xff1a;捕获阶段是 从父到子 冒泡阶段是从子到父…

Linux网络编程01

网络层级 协议 协议&#xff1a;两个对等实体对通话内容的约定&#xff0c;一个协议是对应收发双方相同层级的 常见的协议 应用层&#xff08;公开协议&#xff09;&#xff1a; http协议&#xff08;浏览网页&#xff09;&#xff1b;客户端&#xff08;浏览器&#xff09;发…