C++ 拷贝构造函数
介绍和示例
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
-
通过使用另一个同类型的对象来初始化新创建的对象。
-
复制对象把它作为参数传递给函数。
-
复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
//在这里,obj 是一个对象引用,该对象是用于初始化另一个对象的。
classname (const classname &obj) {// 构造函数的主体
}
示例:
#include <iostream>using namespace std;class Line
{public:int getLength( void );Line( int len ); // 简单的构造函数Line( const Line &obj); // 拷贝构造函数~Line(); // 析构函数private:int *ptr;
};// 成员函数定义,包括构造函数
Line::Line(int len)
{cout << "调用构造函数" << endl;// 为指针分配内存ptr = new int;*ptr = len;
}Line::Line(const Line &obj)
{cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;ptr = new int;*ptr = *obj.ptr; // 拷贝值
}Line::~Line(void)
{cout << "释放内存" << endl;delete ptr;
}
int Line::getLength( void )
{return *ptr;
}void display(Line obj)
{cout << "line 大小 : " << obj.getLength() <<endl;
}// 程序的主函数
int main( )
{Line line(10);display(line);return 0;
}
编译执行结果:
下面的实例对上面的实例稍作修改,通过使用已有的同类型的对象来初始化新创建的对象:
#include <iostream>using namespace std;class Line
{public:int getLength( void );Line( int len ); // 简单的构造函数Line( const Line &obj); // 拷贝构造函数~Line(); // 析构函数private:int *ptr;
};// 成员函数定义,包括构造函数
Line::Line(int len)
{cout << "调用构造函数" << endl;// 为指针分配内存ptr = new int;*ptr = len;
}Line::Line(const Line &obj)
{cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;ptr = new int;*ptr = *obj.ptr; // 拷贝值
}Line::~Line(void)
{cout << "释放内存" << endl;delete ptr;
}
int Line::getLength( void )
{return *ptr;
}void display(Line obj)
{cout << "line 大小 : " << obj.getLength() <<endl;
}// 程序的主函数
int main( )
{Line line1(10);Line line2 = line1; // 这里也调用了拷贝构造函数display(line1);display(line2);return 0;
}
编译执行结果:
拷贝构造函数几个原则:
拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用拷贝构造函数。
C++支持两种初始化形式:
拷贝初始化 int a = 5; 和直接初始化 int a(5); 对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数,也就是说:
A x(2); //直接初始化,调用构造函数 A y = x; //拷贝初始化,调用拷贝构造函数
必须定义拷贝构造函数的情况:
只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义拷贝构造函数也可以拷贝;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义拷贝构造函数。
什么情况使用拷贝构造函数:
类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
- (1)一个对象以值传递的方式传入函数体
- (2)一个对象以值传递的方式从函数返回
- (3)一个对象需要通过另外一个对象进行初始化。
关于为什么当类成员中含有指针类型成员且需要对其分配内存时,一定要有总定义拷贝构造函数??
默认的拷贝构造函数实现的只能是浅拷贝,即直接将原对象的数据成员值依次复制给新对象中对应的数据成员,并没有为新对象另外分配内存资源。
这样,如果对象的数据成员是指针,两个指针对象实际上指向的是同一块内存空间。
在某些情况下,浅拷贝回带来数据安全方面的隐患。
当类的数据成员中有指针类型时,我们就必须定义一个特定的拷贝构造函数,该拷贝构造函数不仅可以实现原对象和新对象之间数据成员的拷贝,而且可以为新的对象分配单独的内存资源,这就是深拷贝构造函数。
如何防止默认拷贝发生
声明一个私有的拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类的对象,编译器会报告错误,从而可以避免按值传递或返回对象。
总结:
当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。所以,这时,必须采用深拷贝。
深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。
相关文章:

C++ 拷贝构造函数
介绍和示例 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象。 复制对象把它作为参数传递给函数。 复制对象…...
怎么使用 Flink 向 Apache Doris 表中写 Bitmap 类型的数据
Bitmap是一种经典的数据结构,用于高效地对大量的二进制数据进行压缩存储和快速查询。Doris支持bitmap数据类型,在Flink计算场景中,可以结合Flink doris Connector对bitmap数据做计算。 社区里很多小伙伴在是Doris Flink Connector的时候&…...
LeetCode 四数相加II 哈希
原题链接: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题面: 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 < i,…...

python(自4) xpath下载 lxml安装 lxml语法 使用方式
(一)安装 搜索xpath 讲解 XPath 教程 (w3school.com.cn) 一,下载地址 : https://chrome.zzzmh.cn/info/hgimnogjllphhhkhlmebbmlgjoejdpjl 二 ,拖拽 (二)lxml安装 cmd 打开终端 cd pythond…...

05-Zookeeper典型使用场景实战
上一篇:04-Zookeeper集群详解 1. Zookeeper 分布式锁加锁原理 如上实现方式在并发问题比较严重的情况下,性能会下降的比较厉害,主要原因是,所有的连接都在对同一个节点进行监听,当服务器检测到删除事件时,…...

stl格式-3D三角形
文章目录 什么是stl文件?格式首选stl的语法1.这是一个stl格式的文件:2.下面先举个例子(难度略微提示)补充:关于\<\<我试了一下:这个法线你随便写好像也没问题\>> 3.来个立方体4.最后再写一个由三个直角形组成的立方体 什么是stl文件? 首先说一下,这个stl不是cpp…...

基于微信小程序的高校暑期社会实践小程序设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言系统主要功能:具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…...

ARM-day2
1、 .text .global _start_start:MOV r0, #0x1ADDS r1,r0, #0x1cmp r1, #100bl funstop:b stopfun:ADD r2,r1,r1.end2、思维导图...

macOS Sonoma 14 正式版(23A344)发布,附黑/白苹果镜像下载地址
系统介绍(系统下载地址:http://www.imacosx.cn/115300.html) 黑果魏叔9 月 27日消息,苹果今日向 Mac 电脑用户推送了 macOS Sonoma 14 正式版(23A344)。 macOS 14正式版系统发布:全新功能与改…...

HarmonyOS开发:封装一个便捷的Log工具类
前言 日志打印,没什么好说的,系统已给我们提供,且调用也是非常的简单,我们封装的目的,一是扩展,打印一些不常见的类型,比如格式化json,使得日志看起来比较好看,二是&…...

第十章_祖冲之_圆周率
倒数1又2/3章,keep_writting的一天: 第十章10.1.7 运行程序资源下载网站为何打不开呢?...

FVM管理Flutter 环境
开发中,会经常切换不同版本的 Flutter 环境。使用 FVM(Flutter Version Manager )来进行 Flutter 版本切换是一个很不错的选择。 下面从安装FVM、环境配置、切换Flutter这几个主要步骤总结下 FVM 的使用 一、安装 homebrew 网址࿱…...

vue | 样式隔离scoped的原理 样式穿透deep的原理
文章目录 vue中的样式穿透scoped 样式隔离父组件可以修改子组件根节点样式 deep 样式穿透:deep(.el-col)的原理 vue中的样式穿透 scoped 样式隔离 一个style标签拥有scoped属性时,css样式就只能作用于当前的组件,这样就可以使得组件之间的样式不互相污…...
UML,集合框架
1.什么是UML UML是统一建模语言的简称,它是一种由一整套图表组成的标准化建模语言。UML用于帮助系统开发人员阐明,展示,构建和记录软件系统的产出。UML代表了一系列在大型而复杂系统建模中被证明是成功的做法,是开发面向对象软件…...

如何快速轻松自动添加微信好友?
有些客需要换新的微信号,想把以前微信号上的好友全部加回来,但是因为微信系统的规定,频繁加好友容易被封号,而且手动添加好友太费时费力,还要控制加好友的间隔时间。那么有没有什么方法可以快速轻松自动添加好友呢&…...

MySQL高级语句(第一部分)
MySQL高级语句(第一部分)一、MySQL进阶查询1、select ----显示表格中一个或数个字段的所有数据记录2、distinct ----不显示重复的数据记录3、where ----有条件查询4、and or ----且 或5、in ----显示已知的值的数据记录6、between ----显示两个值范围内的数据记录7、通配符8、l…...
Perl区分文件换行符类型
背景 在Windows上使用Perl判断文件时何种换行符时,处理CR LF类型的换行符时,也识别成了LF。 思路 Windows上的换行是 CRLF , Unix上是 LF , Mac CR在Windows平台使用Perl读取文件创建文件句柄时,未对file handler设置binmode,了…...

数据备份文件生成--根据表名生成对应的sql语句文件
最近客户有个需求,希望在后台增加手动备份功能,将数据导出下载保存。 当然,此方法不适用于海量数据的备份,这只适用于少量数据的sql备份。 这是我生成的sql文件,以及sql文件里的insert语句,已亲测&#x…...

进程同步与互斥
目录 进程同步与互斥(1) 第一节、进程间相互作用 一、相关进程和无关进程 二、与时间有关的错误 第二节、进程同步与互斥 一、进程的同步 二、进程的互斥 三、临界区 进程同步与互斥(2) 三、信号量与P、V操作的物理含义…...

mysql workbench常用操作
1、No database selected Select the default DB to be used by double-clicking its name in the SCHEMAS list in the sidebar 方法一:双击你要使用的库 方法二:USE 数据库名 2、复制表名,字段名 3、保存链接...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
「Java基本语法」变量的使用
变量定义 变量是程序中存储数据的容器,用于保存可变的数据值。在Java中,变量必须先声明后使用,声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例:声明与初始化 public class VariableDemo {publi…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...

MLP实战二:MLP 实现图像数字多分类
任务 实战(二):MLP 实现图像多分类 基于 mnist 数据集,建立 mlp 模型,实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入,可视化图形数字; 2、完成数据预处理:图像数据维度转换与…...