掘根宝典之C++深复制与浅复制(复制构造函数,默认复制构造函数)

到目前为止我们已经学了构造函数,默认构造函数,析构函数:http://t.csdnimg.cn/EOQxx

转换函数,转换构造函数:http://t.csdnimg.cn/kiHo6

友元函数:http://t.csdnimg.cn/To8Tj

接下来我们来学习一个新函数——复制构造函数

复制构造函数

复制构造函数用于将一个对象复制到新创建的对象中。也就是说它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中

类的复制构造函数原型通常如下:

class name(const class name&);

什么时候调用复制构造函数

新建一个对象并将其初始化为同类现有对象时,复制构造函数将被调用。

这在很多情况下都可能会发生,最常见的情况是将新对象显式的初始化为现有对象。

比如下面这些情况

#include<iostream>
using namespace std;
class AA
{
private:
	int a_;
public:
	AA(const AA& t)
	{
		a_ = t.a_;
	}
	AA(int a)
	{
		a_ = a;
	}
};
int main()
{
	AA t ={2} ;
	AA w = t;//下面4句都将调用复制构造函数
	AA e = AA(t);
	AA* r = new AA(t);
	AA y(t);
}

还有一些情况是每当程序生成了程序副本时,编译器都将使用复制构造函数。

准确的说是当函数按值传递对象和函数返回对象时,都将使用复制构造函数。

我们举个例子

#include<iostream>
using namespace std;
class AA
{
private:
	int a_;
public:
	AA(const AA& t)
	{
		cout << "调用了复制构造函数" << endl;
		a_ = t.a_;
	}
	AA(int a)
	{
		a_ = a;
	}
	void A(AA a)
	{
		cout << a.a_ << endl;
	}
	AA B(AA&t)
	{
		AA w(t);
		return w;
	}
};


int main()
{
	AA e = { 3 };
	AA r = { 9 };
	e.A(r);
	AA t = e.B(r);

}

结果是

调用了复制构造函数
9
调用了复制构造函数

默认的复制构造函数

如果我们没有提供复制构造函数,编译器就会自动提供一个复制构造函数,这个复制构造函数也叫默认复制构造函数。默认的复制构造函数逐个复制非静态成员(成员复制也叫浅复制)

我们可以看个例子

#include<iostream>
using namespace std;
class AA
{
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
};
int main()
{
	AA a = { 9 };
	AA t = a;
	/*与下面的语句等效
	AA t;
	t.a_=a.a_;
	*/
}

浅复制

默认复制构造函数的浅复制

我们先来看这么一个例子

#include<iostream>
using namespace std;
class AA
{
private:
	int*a_;
public:
	AA(int a)
	{
		a_ = new int(a);
	}
	void A()
	{
		cout << *a_ << endl;
	}
};
int main()
{
	AA a(9);
	a.A();//结果是9
	AA t = a;
	t.A();//结果是9

}

结果是 

9
9

可能现在你还没发现什么异样,那我们再看下面这个例子

#include<iostream>
using namespace std;
class AA
{
private:
	int*a_;
public:
	AA(int a)
	{
		a_ = new int(a);
	}
	void A()
	{
		cout << *a_ << endl;
	}
	void shan()
	{
		delete a_;
	}
};
int main()
{
	AA a(9);
	a.A();
	AA t = a;
	t.A();
	a.shan();
	t.A();

}

结果是

9
9
-572662307

我们会发现,啊嘞?第三行怎么是一堆乱码。这是什么情况呢?

原来啊,上面这个情况在对对象进行复制时,只简单地复制对象的成员变量值,而没有复制对象内部的动态分配的资源,这个叫浅复制

这是因为浅复制只复制了指针的值,而没有复制指针所指向的内存。因此,两个对象的a_成员变量指向同一块内存,修改任何一个对象的a_值都会影响到另一个对象。

自定义复制构造函数的浅复制

不用以为我们定义了复制构造函数,进行的复制就不叫浅复制了。实际上,下面这种也叫浅复制。

上例子

#include<iostream>
using namespace std;
class AA
{
private:
	int*a_;
public:
	AA(int a)
	{
		a_ = new int(a);
	}
	AA(const AA&a)
	{
		a_ = a.a_;
	}
	void A()
	{
		cout << *a_ << endl;
	}
	void shan()
	{
		delete a_;
	}
};
int main()
{
	AA a (9);
	a.A();
	AA t = a;
	t.A();
	a.shan();
	t.A();

}

结果是

9
9
-572662307

 惊奇的发现和上面的情况是一样的,这是因为我们定义的复制构造函数也仅仅是复制指针的值罢了,没有开辟新的内存块

浅复制的用武之地

浅复制在某些情况下可能是合适的,例如对于只包含基本类型成员变量的简单对象。但是对于包含动态分配的资源或指针成员变量的对象来说,浅复制可能会导致错误或内存泄漏。在这种情况下,应该使用深复制来保证每个对象都有独立的资源副本。

深复制

解决上面这种类设计中的问题的方法是进行深度复制。也就是说复制构造函数应当复制值并将副本的地址赋给a_成员,而不仅仅是复制值地址。

我们直说可能有点难懂,看个图就知道了

 

 必须定义复制构造函数的原因是一些类成员是使用new初始化的,指向数据的指针,而不是数据本身。

什么时候使用深复制?什么时候用浅复制?

如果类里包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这被称作深度复制。浅复制仅浅浅的复制指针信息,而不会深入复制new出来的那块内存。

实现深复制

实现深复制就必须自己定义一个会另外开辟内存的复制构造函数,而不是简单的逐成员复制

我们看个例子

#include<iostream>
using namespace std;
class AA
{
private:
	int*a_;
public:
	AA(int a)
	{
		a_ = new int(a);
	}
	AA(const AA&a)
	{
		int* w = new int(*a.a_);//深度复制的体现
		a_ = w;
	}
	void A()
	{
		cout << *a_ << endl;
	}
	void shan()
	{
		delete a_;
	}
};
int main()
{
	AA a = { 9 };
	a.A();
	AA t = a;
	t.A();
	a.shan();
	t.A();

}

这便是深度复制了

赋值运算符里的浅复制

我们得先知道,C++允许把一个对象赋值给另一个同类对象

初始化的时候总是会调用复制构造函数,而使用=运算符时也允许调用赋值运算符

将已有对象赋给另一个对象时,将采用重载的赋值运算符

初始化时不一定会使用赋值运算符

使用赋值运算符的情况和那个隐式复制构造函数一样,都是浅复制的问题。

我们可以使用友元函数来重载运算符=,使其成为深度赋值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/386831.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JS逆向六】(下)逆向解密某网站返回的密文的值 仅供学习

逆向日期&#xff1a;2024.02.07 使用工具&#xff1a;Node.js 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 可使用AES进行解密处理&#xff08;直接解密即可&#xff09;&#xff1a;在线AES加解密工具 1、打开某某网站(请使用文章开头的…

利用知识图谱构建医疗问答

1、准备数据集 数据集下载地址&#xff1a;https://github.com/wangle1218/QASystemOnMedicalKG/blob/master/data/medical.json 2、导入相关包 from py2neo import Graph,Node,Relationship # 在cmd中&#xff0c;输入neo4j.bat console并回车 import pandas as pd3、连接N…

(免费领源码)Java#MySql#hadoop高校固定资产管理系统74965-计算机毕业设计项目选题推荐

摘 要 在信息飞速发展的今天&#xff0c;网络已成为人们重要的信息交流平台。高校部门每天都有大量的信息需要通过网络发布&#xff0c;为此&#xff0c;高校固定资产管理系统开发的必然性&#xff0c;所以本人开发了一个基于Tomcat&#xff08;服务器&#xff09;模式的高校固…

四、OpenAI之文本生成模型(Text Generation)

文本生成模型 OpenAI的文本生成模型(也叫做生成预训练的转换器(Generative pre-trained transformers)或大语言模型)已经被训练成可以理解自然语言、代码和图片的模型。模型提供文本的输出作为输入的响应。对这些模型的输入内容也被称作“提示词”。设计提示词的本质是你如何对…

【数据结构】链表OJ面试题5《链表的深度拷贝》(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 后三题在这http://t.csdnimg.cn/gbohQ 给定一个链表&#xff0c;判断链表中是否有环。http://t.csdnimg.cn/Rcdyc 给定一个链表&#xff0c;返回链表开始入环的第一个结点。 如果链表无环&#xff0c;则返回 NULLhttp://t.cs…

23种计模式之Python/Go实现

目录 设计模式what?why?设计模式&#xff1a;设计模式也衍生出了很多的新的种类&#xff0c;不局限于这23种创建类设计模式&#xff08;5种&#xff09;结构类设计模式&#xff08;7种&#xff09;行为类设计模式&#xff08;11种&#xff09; 六大设计原则开闭原则里氏替换原…

springboot(eureka)

把resttemplate注入容器,通过此对象在java代码中发起http请求,用id查询对应的user数据,拿到对应的json,再自动json反序列化成对应对象 这样就实现不同数据库不同访问接口服务器的接口调用(跨服务的远程调用) 集群时候如何做,一个业务模块多个服务器的集群 注册 保存 拉取 eure…

2.14数据结构与算法学习日记

洛谷P1934 封印 题目背景 很久以前&#xff0c;魔界大旱&#xff0c;水井全部干涸&#xff0c;温度也越来越高。为了拯救居民&#xff0c;夜叉族国王龙溟希望能打破神魔之井&#xff0c;进入人界“窃取”水灵珠&#xff0c;以修复大地水脉。可是六界之间皆有封印&#xff0c;…

Leetcode 236.二叉树的最近公共祖先

题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的…

遇到太多的Windows问题怎么办?这里提供几个修复工具

“部署映像服务和管理”工具(DISM)是一个有用且高级的工具,用于扫描、更改和修复任何Windows系统问题。许多操作系统问题,如性能差、启动问题或特定崩溃,都可以归结为损坏的系统文件,而此命令工具能够解决这些问题。 如何检查文件系统 在运行DISM修复之前,重要的是运行…

2024.2.7

1、二叉树的操作 #include<stdio.h> #include<string.h> #include<stdlib.h> typedef char datatype; typedef struct Node {//数据域datatype data;//左孩子指针struct Node *lchild;//右孩子指针struct Node *rchild; }*Btree; Btree create_node() {Btre…

【碎片知识点】安装Linux系统 VMware与kali

天命&#xff1a;VMware就是可以运行操作系统的载体&#xff0c;kali就是Linux的其中一个分支 天命&#xff1a;Linux有两个分支版本&#xff1a;centos与ubuntu&#xff0c;kali底层就是ubuntu&#xff08;所有Linux用起来都差不多&#xff0c;没啥区别&#xff09; 天命&…

Ubuntu 23.10通过APT安装Open vSwitch

正文共&#xff1a;888 字 8 图&#xff0c;预估阅读时间&#xff1a;1 分钟 先拜年&#xff01;祝各位龙年行大运&#xff0c;腾跃展宏图&#xff01; 之前在介绍OpenStack的时候介绍过&#xff08;什么是OpenStack&#xff1f;&#xff09;&#xff0c;OpenStack是一个开源的…

MATLAB知识点:factorial函数(★★★☆☆)计算阶乘

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第…

《小强升职记:时间管理故事书》阅读笔记

目录 前言 一、你的时间都去哪儿了 1.1 你真的很忙吗 1.2 如何记录和分析时间日志 1.3 如何找到自己的价值观 二、无压工作法 2.1 传说中的“四象限法则 2.2 衣柜整理法 三、行动时遇到问题怎么办&#xff1f; 3.1 臣服与拖延 3.2 如何做到要事第一&#xff1f; 3.…

第二十九回 施恩三入死囚牢 武松大闹飞云浦-分布式版本控制系统Git使用

武松要蒋门神答应三件事&#xff1a;离开快活林、东西都归还施恩&#xff0c;公开对施恩赔礼道歉&#xff0c;不许在孟州住。蒋门神不得已都答应了&#xff0c;灰溜溜地离开了孟州城。 一个月之后&#xff0c;天气转凉&#xff0c;张都监调武松到孟州城&#xff0c;做了他的亲…

耳机壳UV树脂制作私模定制耳塞需要哪些工具和材料呢?

制作私模定制耳塞需要使用到一些工具和材料&#xff0c;包括但不限于以下内容&#xff1a; UV树脂&#xff1a;用于制作耳塞的主体部分&#xff0c;具有高硬度、耐磨、耐高温、环保等优点。耳模材料&#xff1a;用于获取用户的耳型&#xff0c;通常是一些快速固化的材料&#…

Kafka(三)(集成SpringBoot)

第三章 Kafka集成 SpringBoot SpringBoot 是一个在 JavaEE 开发中非常常用的组件。可以用于 Kafka 的生产者&#xff0c;也可以 用于 SpringBoot 的消费者。 在初始化springboot环境的时候要勾选kafka依赖 <dependency><groupId>org.springframework.kafka</gr…

代码随想录刷题笔记 DAY 25 | 组合问题 No.77 | 组合求和III No.216 | 电话号码的字母组合 No.17

文章目录 Day 2501. 组合问题&#xff08;No. 77&#xff09;2.1 题目2.2 笔记2.3 代码 02. 组合求和III&#xff08;No. 216&#xff09;2.1 题目2.2 笔记2.3 代码 03. 电话号码的字母组合&#xff08;No. 17&#xff09;3.1 题目3.2 笔记3.3 代码3.4 补充 Day 25 01. 组合问…

uniapp前端手机获取安全区域css值 防止按键不能被点击

引入 再编写小程序和移动端的时候可能会出现这种情况&#xff0c;页面中的按键刚好才手机中按不到的位置 如下 这是苹果手机的home按键 如果刚好我们的按钮再这个位置,用户是点击不到的 我们就需要一个办法,能够自动的让我们的按键移动到安全可点击的区域 解决 我们可以使用…