【C++干货基地】面向对象核心概念与实践原理:拷贝构造函数的全面解读


在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏: 《C++干货基地》《粉丝福利》

⛺️生活的理想,就是为了理想的生活!

引入

  哈喽各位铁汁们好啊,我是博主鸽芷咕《C++干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的城市有没有这种实惠又全面的零食基地呢?C++ 本身作为一门篇底层的一种语言,世面的免费课程大多都没有教明白。所以本篇专栏的内容全是干货让大家从底层了解C++,把更多的知识由抽象到简单通俗易懂。

⛳️ 推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

文章目录

  • 引入
  • ⛳️ 推荐
  • 一、拷贝构造函数的引入
    • 1.1 拷贝构造的概念
  • 二、拷贝构造函数的特征
    • 2.1 拷贝构造的书写形式
    • 2.2 不显示定义自动创建
    • 2.3 浅拷贝与深拷贝
    • 2.4 不申请资源不需要显示定义
  • 三、拷贝构造函数调用场景
    • 3.1 使用已存在对象创建新对象
    • 3.2 函数参数类型为类类型对象
    • 3.3 函数返回值类型为类类型对象
  • 📝文章结语:

一、拷贝构造函数的引入

我们都知道面向对象的对象是一个宏观的概念, 万事万物都可以当成一个对象。而现实中我们的对象是可以复制的,那么我们在编程中创建的对象如何进行复制呢?

在这里插入图片描述

1.1 拷贝构造的概念

在C++中祖师爷规定了:当我们想把一个对象赋值给另一个对象的时候

  • 或者创建一个与已存在对象一某一样的新对象
  • 时需要调用它的拷贝函数来进行复制

在这里插入图片描述
如图所见拷贝构造函数是我们的六大成员默认函数之一,构造函数的作用是初始化,析构函数是复制清理工作,而我们的构造拷贝函数是用来同类对象进行赋值给另一个对象时的工作:

二、拷贝构造函数的特征

2.1 拷贝构造的书写形式

讲了怎么长时间拷贝构造是干什么的,下面就来到拷贝构造的创建把:
其实构造的前几个特征是需要先了解才能去书写的所以博主这里把他都给整合到前面了,后面的其他特征单独介绍:

  • 拷贝构造函数是构造函数的一个重载形式
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,
    因为会引发无穷递归调用。

这里第二个特征就特别强调了,我们在书写拷贝构造函数的时候一定要使用传引用

  • 是因为如果我们传值当形参的话那么形参是实参的一份临时拷贝又会拷贝构造函数
  • 这样就会无限递归下去

🍸 代码演示:

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // Date(const Date d)    // 错误写法:编译报错,会引发无穷递归
    Date(const Date& d)    // 正确写法
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);
 return 0;
}

在这里插入图片描述

  • 所以我们在书写构造函数的时候一定要使用传引用当做形参

2.2 不显示定义自动创建

构造拷贝函数既然是六个默认成员函数之一的话,那么肯定也是符合默认成员函数的特点如果我们没有显示定义的话自动生成:

  • 那么自动生成的拷贝构造函数帮我们完成了什么事情呢?

🍸 代码演示:
在这里插入图片描述

这里我们就可以看到就算我们不写默认成员函数那么编译器也会自动生成 默认拷贝构造函数
去拷贝和赋值,这是可能就有人要问了既然默认生成的 拷贝构造函数 可以完成复制那么为什么要我们手动创建呢?

🔥 这是因为默认生成的拷贝构造函数完成的只是浅拷贝,只是把值复制过去了

2.3 浅拷贝与深拷贝

说到浅拷贝和深拷贝很多铁汁们不太明白,什么是浅拷贝?深拷贝拷贝了那些内容?

  • 下面我们就来看一下这段代码

🍸 代码演示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(capacity * sizeof(int));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}

		_size = 0;
		_capacity = capacity;
	}
	void Push(const int& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}


在这里插入图片描述
这里就是我们说的浅拷贝了,为什么程序回出现崩溃呢?这里刚开始创建了一个S1 对象,又创建了一个S2 对象去进行调用拷贝构造函数进行拷贝:

  • 而这里只进行了浅拷贝,在 S2 进行拷贝构造的时候只是简单的把值复制过去了
  • 所以 S2 和 S1 是指向同一块空间并没有给 S2 去单独开辟一个一模一样的空间

这里就是我们说的浅拷贝,S2 和 S1 指向用一块空间而当程序结束的时候 S2 调用它的析构函数去吧 S1 所指向的空间给释放了, 那么当 S1 在释放的时候就重复释放了原来释放的空间导致程序崩溃。

在这里插入图片描述

🔥 所以在这些去动态申请资源的函数的类去,一定要显示定义拷贝构造函数进行深拷贝

  • 把空间大小和值(内容)完全拷贝进去
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(capacity * sizeof(int));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}

		_size = 0;
		_capacity = capacity;
	}
	Stack(const Stack& p1)
	{
		int* tmp = (int*)malloc(sizeof(int) * p1._capacity);
		if (tmp == nullptr)
		{
			perror("file malloc");
			exit(-1);			

		}

		memcpy(tmp, p1._array, sizeof(int) * p1._size);

		_array = tmp;
		_capacity = p1._capacity;
		_size = p1._size;
		
	}
	void Push(const int& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}


2.4 不申请资源不需要显示定义

如果我们一个类里面并不会去申请资源那么它的默认生成的拷贝构造函数 ,也就够用了。默认生成的拷贝构造函数只会进行值拷贝而我们在不申请资源的话,值拷贝就是我们需要的功能。

  • 🔥 注:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
    时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

三、拷贝构造函数调用场景

到了这里我相信大家一定对靠北构造函数有一定认知了那么大家知道拷贝函数在哪些场景会自动调用呢?

3.1 使用已存在对象创建新对象

这个就是最常见的场景了,使用已存在的对象去创建新对象。

🍸 代码演示:

class Date
{
public:
 Date(int year, int minute, int day)
 {
 cout << "Date(int,int,int):" << this << endl;
 }
 Date(const Date& d)
 {
 cout << "Date(const Date& d):" << this << endl;
 }
 ~Date()
 {
 cout << "~Date():" << this << endl;
 }
private:
 int _year;
 int _month;
 int _day;
};

int main()
{
 Date d1(2022,1,13);
 Test(d1);
 return 0;
}

3.2 函数参数类型为类类型对象

在以前学习函数的时候我们知道,形参是实参的一份临时拷贝所以当函数参数类型为 类 的类型对象的话也会自动调用 拷贝构造函数

🍸 代码演示:

Date Test(Date d)
{
 Date temp(d);
 return temp;
}

3.3 函数返回值类型为类类型对象

函数的返回值返回给我们调用的对象时候,返回的是 temp 的一份临时拷贝并不是对象本身

🍸 代码演示:

Date Test(Date d)
{
 Date temp(d);
 return temp;
}

📝文章结语:

☁️ 看到这里了还不给博主扣个:
⛳️ 点赞🍹收藏 ⭐️ 关注
💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!

在这里插入图片描述

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

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

相关文章

ReorderData - 优化阅读笔记

主要实现文件: bolt/lib/Passes/ReorderData.cpp 支持 X86/Arm 测试用例&#xff1a; bolt/test/reorder-data-writable-ptload.c int a1,a2,a3,a4; // 待补充默认关闭&#xff0c;开启选项&#xff1a; # 指定要重排的数据段 --reorder-data<section1,section2,section3…

基于JavaWeb开发的springboot网咖管理系统[附源码]

基于JavaWeb开发的springboot网咖管理系统[附源码] &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统 &a…

Android14音频进阶:生产者与消费者模型(六十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

力扣每日一题 将标题首字母大写 模拟 String API

Problem: 2129. 将标题首字母大写 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String capitalizeTitle(String title)…

数据结构:栈的创建与使用

今天我们一起来看一种新的数据结构栈&#xff0c;其实这一种结构我们在之前就已经使用过&#xff0c;只是今天我们来强调以下几点&#xff1a; 1、栈是一种数据后进先出的结构 &#xff0c;通过入栈1 2 3 4我们可以得到多种结果 2、我们选用顺序表来实现栈结构&#xff0c;这里…

科技回顾,飞凌嵌入式受邀亮相第八届瑞芯微开发者大会「RKDC2024」

2024年3月7日~8日&#xff0c;第八届瑞芯微开发者大会&#xff08;RKDC2024&#xff09;在福州举行&#xff0c;本届大会以“AI芯片AI应用AloT”为主题&#xff0c;邀请各行业的开发者共启数智化未来。 本届大会亮点颇多&#xff0c;不仅有13大芯片应用展示、9场产品和技术论坛…

软考高级:设计模式分类(创建型、结构型、行为型)概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Linux之防火墙详解

华子目录 什么时防火墙分类Netfilter&#xff08;数据包过滤&#xff09;定义Netfilter分析内容 防火墙无法完成的任务iptables与firewalld区别iptablesiptables执行原则原则防火墙规则规则链概念分析规则链分类注意例&#xff1a;物业管理公司有两条规定&#xff1a; 规则链之…

作用域链的理解(超级详细)

文章目录 一、作用域全局作用域函数作用域块级作用域 二、词法作用域三、作用域链 一、作用域 作用域&#xff0c;即变量&#xff08;变量作用域又称上下文&#xff09;和函数生效&#xff08;能被访问&#xff09;的区域或集合 换句话说&#xff0c;作用域决定了代码区块中变…

C++初学

1>思维导图 2>试编程 提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数要求使用C风格字符串完成 #include <iostream> #include<string.h> using namespace std;int main() {string str;cout <<…

C++14之std::index_sequence和std::make_index_sequence

相关文章系列 std::apply源码分析 C之std::tuple(一) : 使用精讲(全) 目录 1.std::integer_sequence 2.std::index_sequence 3.std::make_index_sequence 4.运用 4.1.打印序列的值 4.2.编译时求值 4.3.std::tuple访问值 5.总结 1.std::integer_sequence 运行时定义一个…

Linux:进程

进程 知识铺垫冯诺依曼体系结构操作系统&#xff08;OS&#xff09; 进程概念进程的查看ps 命令获取进程 pid文件内查看进程终止进程的方式kill命令快捷键 进程的创建 forkfork 返回值问题 进程状态运行状态 &#xff1a;R休眠状态&#xff1a;S &#xff08;可中断&#xff09…

【3GPP】【核心网】【5G】5G核心网组网方案(超详细)

5G NR RRC协议总体介绍 UE入网过程包括几个子过程&#xff1a; UE分为三种状态&#xff1a;空闲态&#xff0c;连接态和非活动态。 开机入网流程 小区搜索与选择 UE开机选网&#xff0c;小区搜索并完成下行同步。 系统消息广播 UE读取广播信息&#x…

VMD + CEEMDAN 二次分解,CNN-LSTM预测模型

目录 往期精彩内容&#xff1a; 前言 1 二次分解与数据集制作 1.1 导入数据 1.2 VMD分解 1.3 样本熵 1.4 CEEMDAN分解 1.5 数据集制作 2 基于Pytorch的 CNN-LSTM 预测模型 2.1 定义CNN-LSTM预测模型 2.2 设置参数&#xff0c;训练模型 3 模型评估与可视化 3.1 结果…

学习JavaEE的日子 Day26 手撕所有集合类底层源码

Day26 1.手撕ArrayList底层源码 思路&#xff1a; 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程 public class Test01 { public static void main(String[] args) {//ArrayList<String> list new ArrayList<>();…

【Java从发入门到精通】Java StringBuffer 和 StringBuilder 类

Java StringBuffer 和 StringBuilder 类 当对字符串进行修改的时候&#xff0c;需要使用 StringBuffer 和 StringBuilder 类。 和 String 类不同的是&#xff0c;StringBuffer 和 StringBuilder 类的对象能够被多次的修改&#xff0c;并且不产生新的未使用对象。 在使用 Stri…

在线安装MySQL5.7

在线安装MySQL 安装MySQL5.7 yum -y install mysql57-community-release-el7-10.noarch.rpm 若无可用安装包&#xff0c;执行下面这句 wget http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm 本地安装 yum localinstall -y mysql57-community-releas…

Head First Design Patterns - 命令模式

什么是命令模式 命令模式&#xff0c;把请求封装成对象&#xff0c;以便使用不同的请求、队列或者日志请求来参数化其他对象&#xff0c;并支持可撤回的操作。 为什么会有命令模式 假设要设置一个遥控器&#xff0c;遥控器需要控制多个设备&#xff0c;每个设备除了开关&#…

MS30517SA单通道、高速、低侧栅极驱动器

产品简述 MS30517SA 是单通道、高速、低侧栅极驱 动器器件&#xff0c;能够有效地驱动 MOSFET 和 IGBT 开 关 。 芯片的 设 计 能 够 大 大 减 少 击 穿 电 流 &#xff0c; MS30517SA 能够提供高峰值拉、灌电流脉冲&#xff0c; 同时提供了轨到轨驱动能力以及低传播…

C break 语句

C 语言中 break 语句有以下两种用法&#xff1a; 当 break 语句出现在一个循环内时&#xff0c;循环会立即终止&#xff0c;且程序流将继续执行紧接着循环的下一条语句。它可用于终止 switch 语句中的一个 case。 如果您使用的是嵌套循环&#xff08;即一个循环内嵌套另一个循…