C++的拷贝构造函数
目录
- 拷贝构造函数
- 一、为什么用拷贝构造
- 二、拷贝构造函数
- 1、概念
- 2、特征
- 1. 拷贝构造函数是构造函数的一个重载形式。
- 2. 拷贝构造函数的参数
- 3. 若未显式定义,编译器会生成默认的拷贝构造函数。
- 4. 拷贝构造函数典型调用场景
拷贝构造函数
一、为什么用拷贝构造
日期类传值(这里是浅拷贝)
#include<iostream>
using namespace std;
class Date {
public:Date(int year=1, int month=1, int day=1) {_year = year;_month = month;_day = day;}void Print() {cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year ;int _month ;int _day ;
};
int main() {Date d1;d1.Print();return 0;
}
运行后:

这里进行了传值的拷贝,形参传给实参,进行了值拷贝,也就是浅拷贝,所以并没有出现问题。
但是栈类的结构浅拷贝会出现问题
#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
int main() {Stack st1;Stack st2(st1);return 0;
}
运行后会变成

从上面我们可以看出程序崩溃了,这是为什么呢?
原因在于我们的栈的结构体类型中有一个指针来指向下一个结构体,当我们进行浅拷贝的时候会将这个地址也拷贝过去,但是我们的c++会自动调用析构函数,那么析构函数就被调用了两次,从上面的图中我们也可以看出来析构函数被调用了两次,所以程序崩溃了。
那么如何解决这个问题呢,我们的C++祖师爷,就定义了一个拷贝构造函数 来解决这个问题。
二、拷贝构造函数
1、概念
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
2、特征
拷贝构造函数也是特殊的成员函数,其特征如下:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数
拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
下面写一个拷贝构造函数重新进行运行:
#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);//要重新开辟空间if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
void Func(Stack stt) {//....
}
int main() {Stack st1;Func(st1);return 0;
}
就会发现是正常运行,如下
//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}
通过拷贝构造函数我们可以看出,是重新开辟了一块空间进行拷贝构造,而且我们使用了引用(&)。那么为什么要用引用呢?
下面我们用日期类函数进行演示:
#include<iostream>
using namespace std;
class Date {
public:Date(int year=1, int month=1, int day=1) {_year = year;_month = month;_day = day;}//错误的拷贝构造函数Date(Date d) {_year = d._year;_month = d._month;_day = d._day;}void Print() {cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year ;int _month ;int _day ;
};
int main() {Date d1;d1.Print();Date d2(d1);d2.Print();return 0;
}
我们会发现代码没有办法运行,进行下面的报错

这是由于当我们d2要对d1进行拷贝构造时发生了以下过程:
规定传值传参都要去调用拷贝构造函数那么,中间就还有临时变量要创建和拷贝,这样一环套一环没有终点。如下:

综上,所以我们要用引用,直接将d1赋值给d,如下所示:

#include<iostream>
using namespace std;
class Date {
public:Date(int year=1, int month=1, int day=1) {_year = year;_month = month;_day = day;}//正确的拷贝构造函数Date(Date& d) {_year = d._year;_month = d._month;_day = d._day;}void Print() {cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year ;int _month ;int _day ;
};
int main() {Date d1;d1.Print();Date d2(d1);d2.Print();return 0;
}
3. 若未显式定义,编译器会生成默认的拷贝构造函数。
默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
所以其实日期类是不用进行我们自己写拷贝构造函数的,因为日期类浅拷贝就够用了,我在上面用日期类进行举例是为了方便我们理解。拷贝构造还是主要用在我开始写的栈类型的程序上。
#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);//要重新开辟空间if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
void Func(Stack stt) {//....
}
int main() {Stack st1;Func(st1);Stack st2(st1);return 0;
}
通过调试上述代码我们发现,它们_a的地址不同,但是_capacity、_top的值是相同的,成功完成了拷贝构造。

运行后的结果如下:

4. 拷贝构造函数典型调用场景
使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象
相关文章:
C++的拷贝构造函数
目录 拷贝构造函数一、为什么用拷贝构造二、拷贝构造函数1、概念2、特征1. 拷贝构造函数是构造函数的一个重载形式。2. 拷贝构造函数的参数3. 若未显式定义,编译器会生成默认的拷贝构造函数。4. 拷贝构造函数典型调用场景 拷贝构造函数 一、为什么用拷贝构造 日期…...
【手机端远程连接服务器】安装和配置cpolar+JuiceSSH:实现手机端远程连接服务器
文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …...
Jupyter Notebook的使用
文章目录 Jupyter Notebook一、Jupyter Notebook是什么?二、使用步骤1.安装Miniconda2.安装启动**Jupyter Notebook**3.一些问题 三、Jupyter Notebook的操作1.更换解释器2.在指定的文件夹中打开3 运行的快捷键 四.报错解决1.画图的时候出现报错2.画图的时候空白3.p…...
vue 使用vue-office预览word、excel,pdf同理
在此,我只使用了docx和excel, pdf我直接使用的iframe进行的展示就不作赘述了 //docx文档预览组件 npm install vue-office/docx//excel文档预览组件 npm install vue-office/excel//pdf文档预览组件 npm install vue-office/pdf如果是vue2.6版本或以下还…...
【Spring Boot 源码学习】RedisAutoConfiguration 详解
Spring Boot 源码学习系列 RedisAutoConfiguration 详解 引言往期内容主要内容1. Spring Data Redis2. RedisAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 redisTemplate 方法2.2.3 stringRedisTemplate 方法 总结 引言 上篇博文࿰…...
Linux中如何进行粘贴复制
因为CTRLC在Linux中具有特定的含义:终止当前操作 xshell提供了CTRLinsert(复制)/shiftinsert(粘贴) 上述快捷键在Windows中依旧支持,...
多输入多输出 | Matlab实现k-means-LSTM(k均值聚类结合长短期记忆神经网络)多输入多输出组合预测
多输入多输出 | Matlab实现k-means-LSTM(k均值聚类结合长短期记忆神经网络)多输入多输出组合预测 目录 多输入多输出 | Matlab实现k-means-LSTM(k均值聚类结合长短期记忆神经网络)多输入多输出组合预测预测效果基本描述程序设计参…...
学习笔记3——JVM基础知识
学习笔记系列开头惯例发布一些寻亲消息 链接:https://baobeihuijia.com/bbhj/contents/3/196593.html JVM(Write Once,Run Anywhere) 以下是一些学习时有用到的资料,只学习了JVM的基础知识,对JVM整体进…...
图像处理:图片二值化学习,以及代码中如何实现
目录 1、了解下图片二值化的含义 2、进行图像二值化处理的方法 3、如何选择合适的阈值进行二值化 4、实现图片二值化(代码) (1)是使用C和OpenCV库实现: (2)纯C代码实现,不要借…...
如果你点击RabbitMQ Service - start了,但http://localhost:15672/#/还是访问不了,那么请看这篇博客!
RabbitMQ 服务启动失败问题小结(Windows环境)_rabbitmq启动不了-CSDN博客...
Shell 脚本学习 day01
release node v1 初始版本 #定义备份目录#当前时间#检查备份目录是否存在,不存在需要创建# 查找并备份 .xxx 文件# 提取文件名(不包含路径部分)# 构建备份文件名# 将查出来的.xxx文件拷贝到备份目录#!/bin/bash # context 备份根目录下所有.…...
esp32 rust linux
官方文档:https://esp-rs.github.io/book/introduction.html 安装 rust curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh 工具 risc: rustup toolchain install nightly --component rust-src # nightly 支持 riscv或使用安装工具同时…...
一文了解Elasticsearch
数据分类 数据按数据结构分类主要有三种:结构化数据、半结构化数据和非结构化数据。 结构化数据 结构化数据具有明确定义数据模型和格式的数据类型。 特点: 数据具有固定的结构和模式。 数据项明确定义数据类型和长度。 适合用于数据查询、过滤和分…...
一篇文章认识【性能测试】
一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始,到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点再可以细分,如对于一个 C/S 软件的响应时间可以细分为网络传输时间、应用服务器处理时间、数据库服务器…...
linux环境mysql安装配置踩坑
背景: 最近公司项目希望改造工作流ACTIVITI5.x的源码框架支持大数据量(历史表单表数据达到10亿), 方案暂定为 1.使用动态数据源 2.将工作流归档历史数据数据保存到一个库中这里定义为读库, 3.在办办件的数据单独一个库…...
相关性网络图 | 热图中添加显著性
一边学习,一边总结,一边分享! 本期教程 写在前面 此图是一位同学看到后,想出的一期教程。 最近,自己的事情比较多,会无暇顾及社群和公众号教程。 1 安装和加载相关的R包 library(ggraph) library(tidy…...
cocosCreator 之 微信小游戏授权设置和调用wxAPI获取用户信息
版本: 3.8.0 语言: TypeScript 环境: Mac 官方文档: 微信官方文档 - 开放能力 微信 API 小游戏环境 在cocosCreator的3.x版本项目开发中,TypeScript最终会被转换为JavaScript语言。 JavaScript的运行时调用的API…...
element ui el-table表格纵向横向滚动条去除并隐藏空白占位列
需求 当table内容列过多时,可通过height属性设置table高度以固定table高度、固定表头,使table内容可以滚动 现在需求是右侧滚动条不好看,需要去除滚动条,并隐藏滚动条所占列的位置 // ----------修改elementui表格的默认样式-…...
防止python进程重复执行
前言 通过保存的进程pid查询上次执行的进程是否退出,决定是否启动新的python进程 代码 pidOption.py import os import psutil pidPath = "saveFile.pid"#写入进程号 def writePid():pid = str(os.getpid())f = open(pidPath, w)f.write(pid...
LV.12 D13 C工程与寄存器封装 学习笔记
一、C语言工程简介 把模板在linux解压出来 代码写在interface.c就可以了。 map.lds是链接脚本文件(负责代码的排布) include中是头文件,src中是写好的源代码 start.s是启动代码,在interface.c之前运行,把cpu和栈做一…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
