C++ 拷贝构造函数

目录

拷贝构造函数概述

拷贝构造函数特性


拷贝构造函数概述

当我们定义好一个类,不做任何处理时,编译器会自动生成以下6个默认成员函数

  • 默认成员函数:如果用户没有手动实现,则编译器会自动生成的成员函数。

同样,拷贝构造函数也属于6个默认成员函数,而且拷贝构造函数构造函数的一种重载形式

  • 拷贝构造函数的功能就如同它的名字——拷贝。我们可以用一个已存在的对象来创建一个与已存在对象一模一样的新的对象
class Date
{
public://构造函数Date(){cout << "Date()" << endl;}//拷贝构造函数Date(const Date& d){cout << "Date()" << endl;_year = d._year;_month = d._month;_day = d._day;}//析构函数~Date(){cout << "~Date()" << endl;}
private:int _year = 0;int _month = 0;int _day = 0;
};void TestDate()
{Date d1;//调用拷贝构造创建对象Date d2(d1);
}

拷贝构造函数特性

拷贝构造函数作为特殊的成员函数同样也有异于常人的特性:

  1. 拷贝构造函数是构造函数的重载
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用。若使用传值的方式,则编译器会报错,因为理论上这会引发无穷递归

错误示例

class Date
{
public://错误示例//如果这样写,编译器就会直接报错,但我们现在假设如果编译器不会检查,//这样的程序执行起来会发生什么Date(const Date d){_year = d._year;_month = d._month;_day = d._day;}
private:int _year = 0;int _month = 0;int _day = 0;
};
void TestDate()
{Date d1;//调用拷贝构造创建对象Date d2(d1);
}

当拷贝构造函数的参数采用传值的方式时,创建对象d2,会调用它的拷贝构造函数d1会作为实参传递给形参d。不巧的是,实参传递给形参本身又是一个拷贝,会再次调用形参的拷贝构造函数…如此便会引发无穷的递归。

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝或者值拷贝

class Date
{
public://构造函数Date(int year = 0, int month = 0, int day = 0){//cout << "Date()" << endl;_year = year;_month = month;_day = day;}//未显式定义拷贝构造函数/*Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}*/void print(){cout << _year << " - " << _month << " - " << _day << endl;}
private:int _year = 0;int _month = 0;int _day = 0;
};void TestDate()
{Date d1(2023, 3, 31);//调用拷贝构造创建对象Date d2(d1);d2.print();
}

有的小伙伴可能会有疑问:编译器默认生成的拷贝构造函数貌似可以很好的完成任务,那么还需要我们手动来实现吗?

答案是:当然需要。Date类只是一个较为简单的类且类成员都是内置类型,可以不需要。但是当类中含有自定义类型时,编译器可就办不了事儿了。

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

class stack
{
public:stack(int defaultCapacity=10){_a = (int*)malloc(sizeof(int)*defaultCapacity);if (_a == nullptr){perror("malloc fail");exit(-1);}_top =  0;_capacity = defaultCapacity;}~stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}void push(int n){_a[_top++] = n;}void print(){for (int i = 0; i < _top; i++){cout << _a[i] << " ";}cout << endl;}
private:int* _a;int _top;int _capacity;
};void TestStack()
{stack s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);s1.print();stack s2(s1);s2.print();s2.push(5);s2.push(6);s2.push(7);s2.push(8);s2.print();
}

如图所示,这段程序的运行结果是程序崩溃了,且通过观察发现,是在第二次析构时出现了错误。其实出现错误的原因是在第二次析构时对野指针进行free了。

一个小tip:多个对象进行析构的顺序如同栈一样,先创建的对象后析构,后创建的对象先析构。

为什么会出现对野指针进行free呢?

原因是,对象s1与对象s2中的成员_a,指向的是同一块空间。在s2析构完成后,这块空间已经被释放,此时的s1._a就是野指针。这就是浅拷贝导致的后果。

编译器默认生成的拷贝构造函数是按字节序拷贝的,在创建s2对象时,仅仅是把s1._a的值赋值给s2._a并没有重新开辟一块与s1._a所指向的空间大小相同内容相同的空间。我们把前者的拷贝方式称为浅拷贝后者称为深拷贝

当开启监视窗口来观察这一过程,我们可以看到s2在进行push时,s1的内容也在跟着改变,且s1._a=s2._a

正确做法

class stack
{
public:stack(int defaultCapacity=10){_a = (int*)malloc(sizeof(int)*defaultCapacity);if (_a == nullptr){perror("malloc fail");exit(-1);}_top =  0;_capacity = defaultCapacity;}//用户自己定义拷贝构造函数stack(const stack& s){_a= (int*)malloc(sizeof(int) * s._capacity);if (_a == nullptr){perror("malloc fail");exit(-1);}memcpy(_a, s._a, sizeof(int) * s._capacity);_top = s._top;_capacity = s._capacity;}~stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}void push(int n){_a[_top++] = n;}void print(){for (int i = 0; i < _top; i++){cout << _a[i] << " ";}cout << endl;}
private:int* _a;int _top;int _capacity;
};

 

5. 拷贝构造函数典型调用场景

  • 使用已存在对象创建新对象;
  • 函数参数类型为类类型对象;
  • 函数返回值类型为类类型对象。

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaWeb(十)

一、JavaWeb概述 Web&#xff1a;全球广域网&#xff0c;也称为万维网(www)&#xff0c;能够通过浏览器访问的网站。 JavaWeb&#xff1a;使用 Java技术进行web互联网开发。 二、JavaWeb 技术栈 2.1、B/S 架构 B/S 架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器…

解决找不到msvcr120.dll无法执行代码的4个方法,快来看看解决方法!

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“缺少xxx.dll文件”。而msvcr120.dll就是其中之一。那么&#xff0c;msvcr120.dll到底是什么呢&#xff1f;它又有什么作用呢&#xff1f;本文将从多个方面对msvcr120.dll进行详细的解析…

华为鸿蒙爆发真实力!原生应用媲美iOS,使用流畅度将提升20至30%

随着华为鸿蒙原生应用开发计划的启动&#xff0c;一场席卷全球的科技浪潮正在涌动。鸿蒙生态的快速发展&#xff0c;吸引了无数企业和开发者的关注&#xff0c;他们纷纷拥抱这个新兴的生态系统&#xff0c;共同构建一个更加繁荣的鸿蒙世界。 华为鸿蒙原生应用开发计划引爆全球…

【电路笔记】-交流电路中的电阻器

交流电路中的电阻器 文章目录 交流电路中的电阻器1、概述2、交流电路中的电阻器示例 13、交流电路中的电阻器示例2 电阻器也可用于交流电源&#xff0c;其中消耗的电压、电流和功率以有效值给出。 1、概述 在之前的文章中&#xff0c;我们研究了电阻器及其连接&#xff0c;并使…

Leetcode刷题笔记题解(C++):BM11 链表相加(二)

思路&#xff1a;先对两个链表进行反转&#xff0c;反转求和注意进位运算&#xff0c;求和完成之后再进行反转得到结果 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/ #include <cstddef> class Soluti…

Excel 删除空白行

目录 一. 方式一: 筛选删除二. 方式二: 定位条件三. 方式三: 隐藏非空白行&#xff0c;删除空白行 一. 方式一: 筛选删除 选中空白行对应的列&#xff0c;按下Ctrl Shift L&#xff0c;给列添加过滤条件。过滤出空白行&#xff0c;然后删除即可。 二. 方式二: 定位条件 按下…

【Qt】QLineEdit显示输入十六进制,位数不足时按照规则填充显示及每两个字符以空格填充

问题 在实际开发中&#xff0c;有时候需要对输入进行限制&#xff0c;一是更加合理&#xff0c;二是防止出现误操作。 比如&#xff1a; 使用Qt进行应用程序开发时&#xff0c;对单行编辑框QLineEdit控件&#xff0c;设置只可输入十六进制。 限制输入的方式常用且经典的是使用…

Linux常用指令详解

目录 前言&#xff1a; Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp&#xff08;copy&#xff09;指令 mv&#xff08;move&#xff09;指令 cat指令 重定向及重定向的类型…

Redis——某马点评day03——part2:秒杀业务异步优化

异步秒杀思路 原本的流程是如下所示&#xff0c;必须从开始到创建订单成功才会返回响应。就像饭店里面从下单到上菜都是一个人在服务&#xff0c;就导致服务员利用率很低&#xff0c;后一个顾客要等到前一个顾客上完菜才可以下单。 最简单的优化就是加员工&#xff0c;一次性…

6.1810: Operating System Engineering 2023 <Lab3: page tables>

一、本节任务 实验环境&#xff1a; 二、要点 如何防止程序破坏内核或其他进程空间&#xff1f;隔离地址空间&#xff0c;进程只能读写自己的内存空间。 在保证隔离的同时&#xff0c;如何将多个地址空间复用到一个物理内存上&#xff1f;虚拟内存/页表。操作系统通过页表来为…

DDSP-SVC-3.0完全指南:一步步教你用AI声音开启音乐之旅

本教程教你怎么使用工具训练数据集推理出你想要转换的声音音频&#xff0c;并且教你处理剪辑伴奏和训练后的音频合并一起&#xff0c;快来试试看把&#xff01; 1.使用的工具 要想训练ai声音&#xff0c;首先需要有各种工具&#xff0c;还需要我们提供你需要训练的声音&#…

Avalonia中如何将View事件映射到ViewModel层

前言 前面的文章里面我们有介绍在Wpf中如何在View层将事件映射到ViewModel层的文章&#xff0c;传送门&#xff0c;既然WPF和Avalonia是两套不同的前端框架&#xff0c;那么WPF里面实现模式肯定在这边就用不了&#xff0c;本篇我们将分享一下如何在Avalonia前端框架下面将事件…

陀螺仪LSM6DSV16X与AI集成(2)----姿态解算

陀螺仪LSM6DSV16X与AI集成.2--姿态解算 概述视频教学样品申请完整代码下载欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码上位机通讯加速度演示陀螺仪工作方式主程序演示 概述 LSM6DSV16X包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff…

多人聊天室

多人聊天包 由于要先创建服务面板&#xff0c;接收客户端连接的信息&#xff0c;此代码使用顺序为先启动服务端&#xff0c;在启动客户端&#xff0c;服务端不用关&#xff0c;不然会报错。多运行几次客户端&#xff0c;实现单人聊天 1.创建服务面板 package yiduiy;import j…

【计算机二级MS Office】word(上)

这里写目录标题 文件选项卡保存和另存为属性检查文档 开始选项卡字体更改字体和字号设置中文和英文为两种不同字体的快捷方式介绍其余图标文本效果突出颜色如何挑选字体颜色字符底纹带圈字符字体对话框&#xff08;隐藏&#xff09; 段落 插入选项卡设计选项卡布局选项卡引用选…

【头歌系统数据库实验】实验6 SQL的多表查询-2

目录 第1关&#xff1a;查询每个选手的信息及其提交的解答信息&#xff0c;没做题的选手不显示 第2关&#xff1a;查询做了1001题且耗时大于500&#xff08;time&#xff09;的选手信息 第3关&#xff1a;查询所有选手信息及其提交的解答信息&#xff0c;没做题的选手也要显…

力扣每日一题:2646. 最小化旅行的价格总和(2023-12-06)

力扣每日一题 题目&#xff1a;2646. 最小化旅行的价格总和 日期&#xff1a;2023-12-06 用时&#xff1a;30 m 14 s 时间&#xff1a;8ms 内存&#xff1a;42.98MB 思路&#xff1a;先统计旅行中每个节点路过的次数&#xff08;dfs方法&#xff09;&#xff0c;再计算减半后的…

项目中使用之Maven BOM

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 工具教程 ✨特色专栏&#xff1a; MyS…

Linux文件部分知识

目录 认识inode 如何理解创建一个空文件&#xff1f; 如何理解对文件写入信息&#xff1f; 如何理解删除一个文件&#xff1f; 为什么拷贝文件的时候很慢&#xff0c;而删除文件的时候很快&#xff1f; 如何理解目录 ​编辑 文件的三个时间 ​编辑 Access&#xff1a; …

Linux系统调试课:网络性能工具总结

文章目录 一、网络性能指标二、netstat三、route四、iptables沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章一起了解下网络性能工具。 一、网络性能指标 从网络性能指标出发,你更容易把性能工具同系统工作原理关联起来,对性能问题有宏观的认识和把握。这样,…