c++ 拷贝构造
我们思考一下这个问题:
观察以下代码,在运行的时候会崩溃
想一想为什么
#include<iostream>
using namespace std;//栈类
typedef int DataType;
class Stack
{
public://默认构造:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc 申请空间失败!!!");return;}_capacity = capacity;_size = 0;}//压栈void Push(DataType data){//CheckCapacity();_array[_size] = data;_size++; }//析构~Stack(){cout << "~Stack()" << endl; free(_array);_array = nullptr;_size = _capacity = 0;}private:DataType* _array;int _capacity;int _size;};void func1(Date d)
{d.Print();
}void func2(Stack s)
{} int main()
{Stack s1;func2(s1);return 0;
}

为什么这种情况下编译的时候会报错?
因为你这里有一个主函数,主函数里面有一个栈类型的变量s1,然后呢这个主函数中我们使用了一个名为func2的函数。这个时候编译器就又会去建立一个func2函数的栈帧。
这个时候要进行传参。
传值方式的传参是一种值的拷贝(或者叫做浅拷贝),意思就是把S1这个空间里的值直接拷贝到 func2这个函数栈帧中。
但是这个时候我们要注意。
我们在主函数的那个S1里面的成员变量_a是一个指针。指向了我们向内存申请的空间。如果我们把这个成员变量_a的值直接拷贝到func2函数中。那么func2函数中也会存取一份指针变量_a的所存的内容(即我们向内存所申请空间的地址)。
由于这是c++,当我们调用了func 2之后,在出这个函数的时候,它会自动调用析构函数。而析构函数会自动释放指针变量所指的那个空间。
于是乎_a所指向的那个空间被我们释放掉了,当我们回到主函数的时候,我们使用完s1结束的时候会再次调用析构函数,这样的话,会造成对同一个空间的再一次释放,所以程序会崩溃。
如何解决:
引用:& (引用其实就是对变量取别名)
void func2(Stack &s)
{}
如果我希望s的改变不去影响s1那该怎么办
即:你希望s 只是 s1 的拷贝
这个时候用一个对象去拷贝另外一个对象的时候,C++定义了一个函数来解决这样一个问题。
这个函数叫做拷贝构造。
拷贝构造函数也是一个特殊的成员函数 其特征如下,
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个,且必须是同类型对象 的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。(你如果不用引用的话,就会造成无穷递归)

函数传参如果是内置类型,那就直接拷贝没问题
如果是自定义类型,则不能直接传,而是要调用一个函数来解决,这个函数就叫拷贝构造
为什么传值传参要调用拷贝构造函数?
为什么传值传参不能像c语言一样,结构体一样把相应的值依次的拷贝过去?
之前说了:像栈这样的类,如果你传值传参按浅拷贝的方式去拷贝的话,则会出现两次调用析构函数的情况,这会造成程序的崩溃。
所以这个时候我们必须要调用一个函数来解决这个问题,这个函数就是拷贝构造函数,他可以帮我们解决两次调用析构函数这个麻烦。这个拷贝构造函数可以完成深拷贝。
是不是有点懵,没关系,我们再来强调一下。
在c语言中,我们函数传值传参是直接拷贝的因为他不会出现一些问题
但是在c++中,如果我们直接拷贝的话,如果是栈这种类型对象的函数它会出现析构函数两次释放同一个空间的情况,
所以这个时候我们的拷贝就不能直接拷贝了,我们需要调用拷贝构造函数来进行拷贝。
我们使用拷贝构造函数来传参的话,那么函数它在传参的时候,它就不会直接传参,他会把参数先给拷贝构造函数,让拷贝构造函数处理完了之后再返回给函数,然后再进行函数中的内容。自定义类型传值传参必须调用拷贝构造。
就是在c++的逻辑中,任何传参都会先调用拷贝构造函数。
所以你会疑惑,为什么会造成深度递归呢?
因为当你使用函数的时候,拷贝构造函数和默认构造函数是构成函数重载的,那么当你调用这个函数的时候,你创建了一个d2嘛,那个d2面后面的括号里面写的是d1
那么他的这个形式是符合函数重载的形式的,这个时候他就会去调用拷贝构造函数,他调用拷贝构造函数的时候,(我们调用函数的时候,第一步先传参,C++的逻辑嘛,传参之前先要传给拷贝构造函数,当拷贝构造函数处理完成之后再传回给调用了的那个函数)那么它就会先把d1的内容传给拷贝构造函数,然后传给拷贝构造函数之后就要进行下一步了,但是当你传给拷贝构造函数之后,你会发现,拷贝后函数又会认为你的这个行为是一个传参的行为,所以他就又会传给拷贝构造函数,就这样一直循环了,死循环了,造成了一个深度的递归
与默认构造函数和析构函数的不同
拷贝构造函数和我们之前学的析构函数和默认构造函数的不同就是当我们不写它的时候,编译器默认生成的拷贝构造函数可以处理内置类型,它会对内置类型进行值拷贝 对自定义类型调用他相应的拷贝。
总结一下
日期类(Date)不需要我们实现拷贝构造,编译器默认生成的就可以直接用了。
但是栈这个类需要我们自己来写一个拷贝构造函数来实现深拷贝,因为编译器默认生成的会出现问题
注意:
//拷贝构造函数 用同类型的对象来初拷贝始化它,所以叫拷贝构造Date(const Date& d){cout << "Date(Date& d)" << endl;_year = d._year;_month = d._month;_day = d._day;}
我们为什么要加一个const?
因为在使用的时候,如果某一天你喝了酒,你把两个变量的位置写反了,这个时候你又没有察觉。那么就会造成一些错误的情况出现。
所以这个时候我们可以在前面加一个const限制一下。
加了const之后,他的权限就变小了,也符合引用的规则
进行引用的时候,权限是可以缩小的,但是是不能放大的
相关文章:
c++ 拷贝构造
我们思考一下这个问题: 观察以下代码,在运行的时候会崩溃 想一想为什么 #include<iostream> using namespace std;//栈类 typedef int DataType; class Stack { public://默认构造:Stack(size_t capacity 3){_array (DataType*)ma…...
MISRA 2012学习笔记(1)-Directives
文章目录 说明Directives2 编译与构建Dir 2.1 3 需求可追溯性Dir 3.1 4 代码设计Dir 4.1Dir 4.2Dir 4.3Dir 4.4Dir 4.5Dir 4.6Dir 4.7Dir 4.8Dir 4.9Dir 4.10Dir 4.11Dir 4.12Dir 4.13 说明 以下等级一般分为三种,建议,必要,强制 建议&#…...
升级node版本后vue2的项目node-sass、sass-loader安装报错(14.x升级到16.x)
node升级到16.x版本后,对应的node-sass需要升级到^6.0.0,此时sass-loader的版本需要升级到10.2.0以上 ,具体对应版本规则可参考链接: https://github.com/webpack-contrib/sass-loader/releases?page3 vue2通过vue/cli创建的项目࿰…...
深入理解CSS选择器:选择正确的方式掌控样式与布局
文章目录 CSS 概括CSS 选择器元素选择器(Element Selector)类选择器(Class Selector)ID 选择器(ID Selector)通用选择器(Universal Selector)属性选择器(Attribute Selec…...
qt设置控件的风格样式
设置tablewidget ui.tableWidget_MaterialLibrary->setStyleSheet("QTableView {""color:#DCDCDC;""background-color: #444444;""border: 1px solid #242424;""alternate-background-color:#525252;""gridline-co…...
简单易懂的Transformer学习笔记
1. 整体概述 2. Encoder 2.1 Embedding 2.2 位置编码 2.2.1 为什么需要位置编码 2.2.2 位置编码公式 2.2.3 为什么位置编码可行 2.3 注意力机制 2.3.1 基本注意力机制 2.3.2 在Trm中是如何操作的 2.3.3 多头注意力机制 2.4 残差网络 2.5 Batch Normal & Layer Narmal 2.…...
C语言经典小游戏之三子棋(超详解释+源码)
“纵有疾风来,人生不言弃,风乍起,合当奋意向此生。” 今天我们一起来学习一下三子棋小游戏用C语言怎么写出来? 三子棋小游戏 1.游戏规则介绍2.游戏准备3.游戏的实现3.1生成菜单3.2游戏的具体实现3.2.1初始化棋盘3.2.2打印棋盘3.2…...
宝塔Linux面板点击SSL闪退打不开?怎么解决?
宝塔Linux面板点击SSL证书闪退如何解决?旧版本的宝塔Linux面板确实存在这种情况,如何解决?升级你的宝塔Linux面板即可。新手站长分享宝塔面板SSL闪退的解决方法: 宝塔面板点击SSL证书闪退解决方法 问题:宝塔Linux面板…...
Problem: 6953. 判断是否能拆分数组
Problem: 6953. 判断是否能拆分数组 文章目录 思路解题方法复杂度Code 思路 针对题目中的以下目标,可以转换寻求数组中是否存在前后两个元素之和>m的情况,如果存在则返回ture,如果不存在则返回false。能这样转换的原因是,如果…...
MobiSys 2023 | 多用户心跳监测的双重成形声学感知
注1:本文系“无线感知论文速递”系列之一,致力于简洁清晰完整地介绍、解读无线感知领域最新的顶会/顶刊论文(包括但不限于 Nature/Science及其子刊; MobiCom, Sigcom, MobiSys, NSDI, SenSys, Ubicomp; JSAC, 雷达学报 等)。本次介绍的论文是:<<MobiSys’23,Multi-User A…...
Netty:ChannelInitializer添加到ChannelPipeline完成任务以后会自动删除自己
说明 io.netty.channel.ChannelInitializer是一个特殊的ChannelInboundHandler。它的主要作用是向 Channel对应的ChannelPipeline中增加ChannelHandler。执行完ChannelInitializer的initChannel(C ch)函数以后,ChannelInitializer就会从ChannelPipeline自动删除自己…...
【VUE】项目本地开启https访问模式(vite4)
在实际开发中,有时候需要项目以https形式进行页面访问/调试,下面介绍下非vue-cli创建的vue项目如何开启https 环境 vue: ^3.2.47vite: ^4.1.4 根据官方文档:开发服务器选项 | Vite 官方中文文档 ps:首次操作,不要被类…...
【状态估计】一维粒子滤波研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
设计模式-迭代器模式在Java中使用示例
场景 为开发一套销售管理系统,在对该系统进行分析和设计时,发现经常需要对系统中的商品数据、客户数据等进行遍历, 为了复用这些遍历代码,开发人员设计了一个抽象的数据集合类AbstractObjectList,而将存储商品和客户…...
Maven入职学习
一、什么是Maven? 概念: Maven是一种框架。它可以用作依赖管理工具、构建工具。 它可以管理jar包的规模、jar包的来源、jar包之间的依赖关系。 它的用途就是管理规模庞大的jar包,脱离IDE环境执行构建操作。 具体使用: 工作机…...
【多音音频测试信号】具有指定采样率和样本数的多音信号,生成多音信号的相位降低波峰因数研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
LeetCode150道面试经典题-删除有序数组中的重复项(简单)
1.题目 给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k ,…...
人大金仓数据库Docker部署
docker 搭建 yum -y install yum-utilsyum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposystemctl start docker.servicesystemctl enable docker.servicesystemctl status docker.service 配置Docker cd /etc/docker/ vi da…...
Leetcode-每日一题【剑指 Offer 07. 重建二叉树】
题目 输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 示例 1: Input: preorder [3,9,20,15,7], inorder [9,3,15,20,7]Output: [3,9,20,null,null,15,7] 示例 2: Input: preo…...
Shell编程快速入门
Shell编程快速入门 脚本格式要求 脚本以#!/bin/bash开头脚本需要有可执行权限 脚本的常用执行方式 方式1:输入脚本的绝对路径或相对路径方式2:sh脚本 Shell的变量 Shell变量介绍 Linux Shell中的变量分为系统变量和用户自定义变量 系统变量&#…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
