拷贝构造函数

server/2025/1/15 14:07:28/

文章目录

  • 一、4. 拷贝构造函数


今天我们来学习拷贝构造函数

在这里插入图片描述

一、4. 拷贝构造函数

如果⼀个构造函数的第⼀个参数是自身类型的引用,且任何额外的参数都有默认值,则此叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。
它的形式是这样的:

#include<iostream>
using namespace std;
class Date
{ 
//拷贝构造函数		
Date(const Date & d)
{_year = d._year;_month = d._month;_day = d._day;
}
private:int _year;int _month;int _day;
};

拷贝构造具有以下特点:

  1. 拷贝构造函数是构造函数的⼀个重载。

拷贝构造函数和普通构造函数一样,名字与类名相同,并且也是用于创建对象(用已存在的对象创建新对象)。它与其他构造函数的区别在于参数类型,它的参数是同类对象的引用。拷贝构造函数通过其特殊的参数类型(类对象的引用)与其他构造函数形成了重载关系。编译器可以根据调用时的参数情况(是普通参数还是类对象引用)来决定调用哪一个构造函数。

 Date(int year = 1, int month = 1, int day = 1)
{_year = year;_month = month;_day = day;}Date(const Date & d)
{_year = d._year;_month = d._month;_day = d._day;
}int main()
{
//既初始化了对象,又调用了拷贝构造Date d1();return 0;
}
  1. 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值。

Date &d 是为引用,目的是防止程序循环拷贝构造。如果为(Date d),则主程序Date d2(d1)时将d1拷贝赋值给了d,每次要调用烤贝构造函数之前要先传值传参,传值传参是一种烤贝,又形成一个新的拷贝构造,就形成了无穷递归。
const目的是为了保证被引用的对象不因拷贝的改变而改变。

#include<iostream>
using namespace std;
class Date
{ public:Date(int year = 1, int month = 1, int day = 1)
{_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;int _month;int _day;
};int main()
{Date d1(2024, 7, 5);Date d2(d1);d2.Print();return 0;
}

在这里插入图片描述


  1. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。

C++把类型分成内置类型(基本类型)和自定义类型
内置类型 :就是语言提供的原生数据类型,如:int/char/double/指针等。
自定义类型 :就是我们使用class/struct等关键字自己定义的类型。

#include<iostream>
using namespace std;
class Date
{
public:Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;cout << " const Date& d" << endl;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};void Func1(Date d)
{}int main()
{Date d1;Func1(d1);return 0;
}

在这里插入图片描述
该例子说明了Func1函数调用了拷贝函数构造。
原因是Func1(Date d),Date d 调用了拷贝。


  1. 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。
#include<iostream>
using namespace std;
class Date
{public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}		void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);d2.Print();return 0;
}

在这里插入图片描述


  1. Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源(malloc),生成了空间,浅拷贝会将地址也拷贝成一样的。
    一个修改会影响另外一个

会让两个对象指向同一块空间,堆空间释放两次,会报错。所以编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的拷贝构造会调用Stack的拷贝构造,也不需要我们显示实现MyQueue的拷贝构造。

这里还有一个小技巧,如果一个类显示实现了析构并释放资源,那么他就需要显示写拷贝构造(因为释放了资源就说明当初开了资源,如果不写拷贝构造,那么系统自动生成的拷贝构造会由于浅拷贝而出错),否则就不需要。

#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_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;int _month;int _day;
};typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}// st2(st1)Stack(const Stack& st){_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}void Pop(){_a[_top - 1] = -1;--_top;}int Top(){return _a[_top - 1];}~Stack(){cout << "~Stack()" << endl;free(_a);}
private:STDataType* _a;size_t _capacity;size_t _top;
};class MyQueue
{
private:Stack _pushst;Stack _popst;
};

  1. 传值返回,会产生一个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。但是如果返回对象是一个当前函数的局部域的局部对象,函数结束就销毁了,原来的那块空间释放了,那么使用引用返回是有问题的,这时的引|用相当于一个野引用,类似一个野指针一样。会非常危险。

传引用返回可以减少拷贝,但是一定要确保返回对象,在当前函数结束后还在,才能用引用返回。不能用局部变量当返回对象


感谢大家能看到这里,多多支持!

在这里插入图片描述


http://www.ppmy.cn/server/158271.html

相关文章

docker虚拟机平台未启用问题

在终端中输入如下代码&#xff0c;重启电脑即可 Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform 对于Docker Desktop - Unexpected WSL error问题 参考链接 解决WSL2与docker冲突问题

青少年编程与数学 02-006 前端开发框架VUE 24课题、UI表单

青少年编程与数学 02-006 前端开发框架VUE 24课题、UI表单 一、UI表单二、Element Plus表单1. 安装Element Plus2. 配置Element Plus3. 创建表单组件4. 在App.vue中使用表单组件5. 运行项目 三、Vuetify表单1. 安装Vuetify2. 配置Vuetify3. 创建表单组件4. 在App.vue中使用表单…

Untiy中如何嵌入前端页面,从而播放推流视频?

最近工作中频繁用到unity,虽然楼主之前也搞过一些UNTY游戏开发项目&#xff0c;但对于视频这块还是不太了解&#xff0c;所以我们采用的方案是在Unity里寻找一个插件来播放推流视频。经过我的一番寻找&#xff0c;发现了这款Vuplex 3D WebView&#xff0c;它可以完美的打通Unit…

【Excel】【VBA】根据某列的编号顺序筛选对应的行导入相应的sheet中

Excel VBA 数据分类导入sheet 1. 程序功能 将Excel表格数据按照PC编号分类到不同Sheet。 2. 程序流程 #mermaid-svg-mKz9P9fN9JJgn75t {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-mKz9P9fN9JJgn75t .error-ic…

iOS - runtime总结

详细总结一下 Runtime 的核心内容&#xff1a; 1. 消息发送机制 // 消息发送的基本流程 id objc_msgSend(id self, SEL _cmd, ...) {// 1. 获取 isaClass cls object_getClass(self);// 2. 查找缓存IMP imp cache_getImp(cls, _cmd);if (imp) return imp(self, _cmd, ...);…

开源AI模型的优势、挑战与未来发展分析

开放源代码模型在灵活性和生态系统方面具有竞争力&#xff0c;可能会超越闭源API 开放源代码模型在灵活性和生态系统方面的竞争力主要体现在以下几个方面&#xff1a; 1. 灵活性 定制化能力: 开放源代码模型允许用户根据特定需求进行修改和调整。这种灵活性使得开发者能够根…

【YOLOv5】源码(train.py)

train.py是YOLOv5中用于模型训练的脚本文件&#xff0c;其主要功能是读取配置文件、设置训练参数、构建模型结构、加载数据、训练/验证模型、保存模型权重文件、输出日志等 参考笔记&#xff1a; 【YOLOv3】源码&#xff08;train.py&#xff09;_yolo原始代码-CSDN博客 【y…

Blazor开发复杂信息管理系统的优势

随着现代企业信息管理需求的不断提升&#xff0c;开发高效、易维护、可扩展的系统变得尤为重要。在这个过程中&#xff0c;Blazor作为一种新兴的Web开发框架&#xff0c;因其独特的优势&#xff0c;逐渐成为开发复杂信息管理系统的首选技术之一。本文将结合Blazor在开发复杂信息…