C++ 类和对象 拷贝构造函数
一 拷贝构造函数的概念:
拷贝构造函数是一种特殊的构造函数,用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时,或者将对象传递给函数或从函数返回对象时,会调用拷贝构造函数。
二 拷贝构造函数的特点:
1:拷贝构造函数是构造函数的一个重载形式。
2:拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
3:若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定 义类型是调用其拷贝构造函数完成拷贝的。
4:编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗? 当然像日期类这样的类是没必要的。
2.1 代码示例:
class Time
{
public:// 普通构造函数Time(int hour = 0, int minute = 0, int second = 0) {_hour = hour;_minute = minute;_second = second;}// 拷贝构造函数,使用引用传递Time(const Time& other) {_hour = other._hour;_minute = other._minute;_second = other._second;}void Print() const {std::cout << _hour << ":" << _minute << ":" << _second << std::endl;}private:int _hour;int _minute;int _second;
};int main()
{Time t1(10, 20, 30); // 使用普通构造函数//构造函数的重载Time t2 = t1; // 使用拷贝构造函数//Time t2(t1); // 拷贝构造的另一种写法t1.Print();t2.Print();return 0;
}
输出:

2.2 为什么要使用引用呢?
我们在 increment 函数中改变x的值并没有间接性改变a,这是因为传过去的只是编译器创建实参的一个副本,而修改副本怎么可能可以改变a呢?
#include <iostream>void increment(int x)
{x = x + 1; // 修改的是副本,不影响实参
}int main()
{int a = 5;increment(a); // 传递a的副本std::cout << a << std::endl; // 输出5,原始值a未被修改return 0;
}
知道传值传参的本质之后,再来想一想为什么要用引用?咱们先来说说如果没用用引用的后果会是怎么样,当把自定义类型传出去后且不用引用或者指针来接收,它会
调用 Time(const Time other),其中 other 是 t1 的按值传递副本。
为了按值传递,编译器需要创建 other 的副本。
创建 other 的副本时,再次调用 Time(const Time other)。
这个新调用的 Time(const Time other) 又需要创建自己的 other 副本,再次调用 Time(const Time other)。
如此反复,导致无限递归调用,最终导致栈溢出。
图:

C++规定,自定义类型的拷贝,都会调用拷贝构造
那为什么要引用呢?
首先我们来回顾一下引用 :
1:引用是现有变量的另一个名字。
2:它们不创建新对象,只是指向已有对象。
3:引用只是指向现有对象,不创建新副本
因为引用就是它本身,所以何来创建新副本这一说法,创建新副本是怕改变副本从而导致改变实参值
2.3 总结:
1:按值传递会递归:每次传递对象会复制对象,导致无限递归。
2:引用传递避免递归:引用只是指向对象本身,不会复制对象
三 默认拷贝构造:
当你没有显式定义拷贝构造函数时,编译器会为你自动生成一个默认的拷贝构造函数。这个默认拷贝构造函数会逐个拷贝对象的所有成员变量。
3.1 内置类型与自定义类型的拷贝:
内置类型:如 int, char, float 等,拷贝时直接按照字节方式进行复制,也就是直接复制其值。
自定义类型:如类和结构体,拷贝时会调用该类型的拷贝构造函数。
3.2 代码示例:
内置类型:
#include <iostream>class MyClass
{
public:int x; // 内置类型成员
};int main()
{MyClass obj1;obj1.x = 10;MyClass obj2 = obj1; // 使用编译器生成的默认拷贝构造函数std::cout << "obj1.x: " << obj1.x << std::endl; std::cout << "obj2.x: " << obj2.x << std::endl;return 0;
}
输出:

对于一个类里面只有内置类型成员那编译器生成的默认拷贝构造会自动复制其值。
自定义类型:
#include <iostream>class Time
{
public:// 默认构造函数Time() { _hour = 0;_minute = 0;_second = 0;}// 拷贝构造函数Time(const Time& other) {_hour = other._hour;_minute = other._minute;_second = other._second;std::cout << "Time::Time(const Time& other)" << std::endl;}private:int _hour;int _minute;int _second;
};class MyClass
{
public:int x; // 内置类型成员Time t; // 自定义类型成员
};int main()
{MyClass obj1;obj1.x = 10;MyClass obj2 = obj1; // 使用编译器生成的默认拷贝构造函数std::cout << "obj1.x: " << obj1.x << std::endl;std::cout << "obj2.x: " << obj2.x << std::endl; return 0;
}
当执行MyClass obj2 = obj1; 因obj1类里面有自定义类型 t 所以编译器生成的默认拷贝构造会自动调用Time(const Time& other) 来完成
3.3 总结:
内置类型:编译器默认拷贝构造函数会直接复制其值。
自定义类型:编译器默认拷贝构造函数会调用该类型的拷贝构造函数来复制其内容。
四 内存分区:
要理解好深拷贝与浅拷贝那就得先了解内存是怎么样分区的。

计算机程序运行时,内存通常被分为四个主要区域:栈区、堆区、全局静态区和只读区(常量区和代码区)。
4.1 栈区:
局部变量:函数内部定义的变量。
形参(函数参数):函数定义时的参数。
返回地址:函数调用后的返回地址。
特点:
栈区中访问速度快且栈的内存连续分配。
因存储的都是 局部/形参/返回地址 所以栈区空间小,存储的生命周期短。
在我们局部变量所在的函数执行完成时,它会自动释放内存。
4.2 堆区:
动态分配的数据:通过 new 或 malloc 等动态分配函数分配的内存。
特点:
因存储的都是new 或者malloc开辟的空间所以堆区空间大,所以访问速度慢。
堆中的内存分配和释放是通过指针进行的,可能不是连续的。
堆区的内存需要程序员手动管理,必须手动释放动态分配的内存,否则会导致内存泄漏。
4.3 全区/静态区:
全局变量:在所有函数外部定义的变量。
静态变量:使用 static 关键字定义的变量。
特点:
全局变量和静态变量在程序的整个运行期间一直存在,直到程序结束。
全局变量可以在程序的所有函数中访问,静态变量在声明的作用域内共享。
4.4 只读常量区:
常量:程序中定义的常量。
代码:程序的指令代码。
特点:
常量区的数据在程序运行期间不能被修改,保证了数据的安全性和稳定性。
代码区存储程序的指令代码,在程序运行时被载入内存以执行。
五 浅拷贝:
首先我们来回顾C语言里面的基本类型和指针类型。
5.1 基本类型:
基本类型是C语言内置的数据类型,它们用于存储最基本的数值数据。常见的基本类型包括:int float char……
5.2 指针类型:
指针类型是存储内存地址的数据类型。指针用于指向其他变量或对象在内存中的位置。
5.3 基本类型代码示例:
#include <iostream>class BasicType
{
public:int value;// 构造函数BasicType(int v) {value = v;}// 拷贝构造函数BasicType(const BasicType& other) {value = other.value;}
};int main()
{BasicType obj1(10);BasicType obj2 = obj1; // 浅拷贝,复制基本类型的值std::cout << "改变前: " << std::endl;std::cout << "obj1.value: " << obj1.value << std::endl;std::cout << "obj2.value: " << obj2.value << std::endl;obj2.value = 20; // 修改obj2的值std::cout << "改变后: " << std::endl;std::cout << "obj1.value: " << obj1.value << std::endl;std::cout << "obj2.value: " << obj2.value << std::endl;return 0;
}
输出:

值会被复制但修改新对象的值不会影响原对象。
5.3 指针类型代码示例:
#include <iostream>class SimplePointer
{
public:int* ptr; // 成员变量 ptr// 构造函数SimplePointer(int value)
{ptr = (int*)malloc(sizeof(int)); // 动态分配内存并初始化if (ptr != nullptr) {*ptr = value;}
}SimplePointer(const SimplePointer& other) {this->ptr = other.ptr; // 浅拷贝,复制内存地址}void print() const {std::cout << "Value: " << *ptr << std::endl;}
};int main()
{SimplePointer obj1(10); // 创建第一个对象,并将值初始化为10SimplePointer obj2(obj1); // 使用拷贝构造函数(浅拷贝)// 打印初始值std::cout << "Initial values:" << std::endl;obj1.print();obj2.print();// 修改obj2的值*obj2.ptr = 20;// 打印修改后的值std::cout << "After change:" << std::endl;obj1.print();obj2.print(); return 0;
}
输出:

复制内存地址,共享同一块内存,修改会互相影响
六 深拷贝:
#include <iostream>
#include <cstdlib>
#include <cstring>class SimpleClass
{
public:int* ptr;// 默认构造函数SimpleClass(int value) {ptr = (int*)malloc(sizeof(int)); // 动态分配内存并初始化if (ptr != nullptr) {*ptr = value;}}// 深拷贝构造函数SimpleClass(const SimpleClass& other) {ptr = (int*)malloc(sizeof(int)); // 分配新内存if (ptr != nullptr) {*ptr = *(other.ptr); // 复制内容}}// 析构函数~SimpleClass() {if (ptr != nullptr) {free(ptr); // 释放内存}}void Print() const {if (ptr != nullptr) {std::cout << "Value: " << *ptr << std::endl;}}
};int main()
{SimpleClass obj1(10); // 创建对象,ptr 指向的值为 10SimpleClass obj2 = obj1; // 使用深拷贝构造函数obj1.Print();obj2.Print();// 修改 obj2 的值if (obj2.ptr != nullptr) {*(obj2.ptr) = 20;}obj1.Print();obj2.Print();return 0;
}
输出:
深拷贝不仅复制对象的指针成员,还为指针指向的内容分配新的内存,并复制原对象的数据。这样,两个对象拥有独立的内存,修改一个不会影响另一个。
相关文章:
C++ 类和对象 拷贝构造函数
一 拷贝构造函数的概念: 拷贝构造函数是一种特殊的构造函数,用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时,或者将对象传递给函数或从函数返回对象时,会调用拷贝构造函数。 二 拷贝构造函…...
C# —— Math对象
Math 数学类 提供了一些相关数学计算的属性和方法、四舍五入、向上求整、向下求整、开平方,几次方 最大值和最小值 sin cos 绝对值 方法 1.Math 常用的字段 Math.PI double x 2 * 180 / Math.PI; Console.WriteLine(x); 2 Math.Abs() 求绝对值 int a -3; Con…...
Face_recognition实现人脸识别
这里写自定义目录标题 欢迎使用Markdown编辑器一、安装人脸识别库face_recognition1.1 安装cmake1.2 安装dlib库1.3 安装face_recognition 二、3个常用的人脸识别案例2.1 识别并绘制人脸框2.2 提取并绘制人脸关键点2.3 人脸匹配及标注 欢迎使用Markdown编辑器 本文基于face_re…...
1-3分钟爆款视频素材在哪找啊?这9个热门爆款素材网站分享给你
在如今快节奏的时代,短视频已成为吸引观众注意力的黄金手段。然而,要制作出1-3分钟的爆款视频,除了创意和剪辑技巧外,选择合适的素材至关重要。那么,哪里可以找到那些能让你的视频脱颖而出的爆款素材呢?不用…...
武汉免费 【FPGA实战训练】 Vivado入门与设计师资课程
一.背景介绍 当今高度数字化和智能化的工业领域,对高效、灵活且可靠的技术解决方案的需求日益迫切。随着工业 4.0 时代的到来,工业生产过程正经历着前所未有的变革,从传统的机械化、自动化逐步迈向智能化和信息化。在这一背景下&…...
【vite创建项目】
搭建vue3tsvitepinia框架 一、安装vite并创建项目1、用vite构建项目2、配置vite3、找不到模块 “path“ 或其相对应的类型声明。 二、安装element-plus1、安装element-plus2、引入框架 三、安装sass sass-loader1、安装sass 四、安装vue-router-next 路由1、安装vue-router42搭…...
最优化方法 运筹学【】
1.无约束 常用公式 线搜索准则:求步长 精确线搜索(argmin) 最速下降:sd:线性收敛 2.算法 SD dk:付梯度-g newton dk:Gkd-g 二阶收敛,步长为1 阻尼牛顿:步长用先搜…...
探索 WebKit 的动感世界:设备方向和运动支持全解析
探索 WebKit 的动感世界:设备方向和运动支持全解析 随着移动设备的普及,网页应用对设备方向和运动的感知需求日益增长。WebKit 作为众多流行移动浏览器的渲染引擎,提供了对设备方向和运动的全面支持,使得 Web 应用能够根据设备的…...
高考假期预习指南
IT专业入门,高考假期预习指南 对于希望进入IT行业的学生来说,假期是学习信息技术的最佳时机。 在信息化快速发展的时代,IT行业的发展前景广阔,但高技能要求使新生可能感到迷茫。 建议新生制定详细的学习计划,包括了解…...
Spring Boot 事件监听机制工作原理
前言: 我们知道在 Spring 、Spring Boot 的启动源码中都大量的使用了事件监听机制,也就是我们说的的监听器,监听器的实现基于观察者模式,也就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦&#…...
【AI大模型】驱动的未来:穿戴设备如何革新血液、皮肤检测与营养健康管理
文章目录 1. 引言2. 现状与挑战3. AI大模型与穿戴设备概述4. 数据采集与预处理4.1 数据集成与增强4.2 数据清洗与异常检测 5. 模型架构与训练5.1 高级模型架构5.2 模型训练与调优 6. 个性化营养建议系统6.1 营养建议生成优化6.2 用户反馈与系统优化 7. 关键血液成分与健康状况评…...
【FFmpeg】avcodec_open2函数
目录 1. avcodec_open21.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)1.2 编解码器的初始化(init)1.3 释放编解码器(ff_codec_close) FFmpeg相关记录: 示例工程ÿ…...
matlab:对带参数a关于x的方程求解
题目 讲解 简洁对各个式子的内部含义用浅显易懂的话语总结出来了,耐心体会 f(a) (x)exp(x)x^ax^(sqrt(x))-100;%因为下面的fzero的第一个数需要一个fun,所以这里有两个句柄,第一个a是输入的,第二个x是需要被解出的 A0:0.1:2;%创…...
Yolov10训练,转化onnx,推理
yolov10对于大目标的效果好,小目标不好 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…...
GEE代码实例教程详解:洪水灾害监测
简介 在本篇博客中,我们将使用Google Earth Engine (GEE) 进行洪水灾害监测。通过分析Sentinel-1雷达数据,我们可以识别特定时间段内的洪水变化情况。 背景知识 Sentinel-1数据集 Sentinel-1是欧洲空间局提供的雷达卫星数据集,它能够提供…...
运维锅总详解系统设计原则
本文对CAP、BASE、ACID、SOLID 原则、12-Factor 应用方法论等12种系统设计原则进行分析举例,希望对您在进行系统设计、理解系统运行背后遵循的原理有所帮助! 一、CAP、BASE、ACID简介 以下是 ACID、CAP 和 BASE 系统设计原则的详细说明及其应用举例&am…...
深度学习笔记: 最详尽解释预测系统的分类指标(精确率、召回率和 F1 值)
欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家! 预测系统的分类指标(精确率、召回率和 F1 值) 简介 让我们来谈谈预测系统的分类指标以及对精确率、召回…...
GEE代码实例教程详解:MODIS土地覆盖分类与面积计算
简介 在本篇博客中,我们将使用Google Earth Engine (GEE) 对MODIS土地覆盖数据进行分析。通过MODIS/061/MCD12Q1数据集,我们可以识别不同的土地覆盖类型,并计算每种类型的总面积。 背景知识 MODIS MCD12Q1数据集 MODIS/061/MCD12Q1是NASA…...
LT86101UXE 国产原装 HDMI2.0 / DVI中继器方案 分辨率 4Kx2K 用于多显示器 DVI/HDMI电缆扩展模块
1. 描述 Lontium LT86101UXE HDMI2.0 / DVI中继器特性高速中继器符合HDMI2.0/1.4规范,最大6 gbps高速数据率、自适应均衡RX输入和pre-emphasized TX输出支持长电缆应用程序,没有晶体在船上保存BOM成本,内部灵活的PCB TX巷交换路由。 LT86101UXE HDMI2.0/DVI中继器自动检测线缆损…...
FastApi中的常见请求类型
FastApi中的常见请求类型 后端开发语言中,我钟情于node,高效的异步处理真是让我眼前一亮,同时,简单易懂的语法也让我非常倾心 但是但是,因为考虑要写一个深度学习算法的后端接口,所以不得不选用python作为…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
