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中的变量分为系统变量和用户自定义变量 系统变量&#…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
