当前位置: 首页 > news >正文

C++的拷贝构造函数

目录

  • 拷贝构造函数
    • 一、为什么用拷贝构造
    • 二、拷贝构造函数
      • 1、概念
      • 2、特征
        • 1. 拷贝构造函数是构造函数的一个重载形式。
        • 2. 拷贝构造函数的参数
        • 3. 若未显式定义,编译器会生成默认的拷贝构造函数。
        • 4. 拷贝构造函数典型调用场景

拷贝构造函数

一、为什么用拷贝构造

日期类传值(这里是浅拷贝)

#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;d1.Print();return 0;
}

运行后:
在这里插入图片描述

这里进行了传值的拷贝,形参传给实参,进行了值拷贝,也就是浅拷贝,所以并没有出现问题。

但是栈类的结构浅拷贝会出现问题

#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
int main() {Stack st1;Stack st2(st1);return 0;
}

运行后会变成
在这里插入图片描述
从上面我们可以看出程序崩溃了,这是为什么呢?
原因在于我们的栈的结构体类型中有一个指针来指向下一个结构体,当我们进行浅拷贝的时候会将这个地址也拷贝过去,但是我们的c++会自动调用析构函数,那么析构函数就被调用了两次,从上面的图中我们也可以看出来析构函数被调用了两次,所以程序崩溃了。

那么如何解决这个问题呢,我们的C++祖师爷,就定义了一个拷贝构造函数 来解决这个问题。

二、拷贝构造函数

1、概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2、特征

拷贝构造函数也是特殊的成员函数,其特征如下:

1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数

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

下面写一个拷贝构造函数重新进行运行:

#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);//要重新开辟空间if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
void Func(Stack stt) {//....
}
int main() {Stack st1;Func(st1);return 0;
}

就会发现是正常运行,如下在这里插入图片描述

		//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}

通过拷贝构造函数我们可以看出,是重新开辟了一块空间进行拷贝构造,而且我们使用了引用(&)。那么为什么要用引用呢?
下面我们用日期类函数进行演示:

#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(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;d1.Print();Date d2(d1);d2.Print();return 0;
}

我们会发现代码没有办法运行,进行下面的报错
在这里插入图片描述
这是由于当我们d2要对d1进行拷贝构造时发生了以下过程:
规定传值传参都要去调用拷贝构造函数那么,中间就还有临时变量要创建和拷贝,这样一环套一环没有终点。如下:
在这里插入图片描述
综上,所以我们要用引用,直接将d1赋值给d,如下所示:
在这里插入图片描述

#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(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;d1.Print();Date d2(d1);d2.Print();return 0;
}
3. 若未显式定义,编译器会生成默认的拷贝构造函数。

默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

所以其实日期类是不用进行我们自己写拷贝构造函数的,因为日期类浅拷贝就够用了,我在上面用日期类进行举例是为了方便我们理解。拷贝构造还是主要用在我开始写的栈类型的程序上。

#include<iostream>
using namespace std;class Stack{public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_a = (int*)malloc(sizeof(int) * capacity);//要重新开辟空间if (nullptr == _a){perror("malloc申请空间失败!!!");}_capacity = capacity;_top = 0;}//拷贝构造函数Stack(const Stack& stt ) {_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr) {perror("malloc");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_capacity = stt._capacity;_top = stt._top;}~Stack(){cout << "~Stack()" << endl;free(_a);_capacity = _top = 0;_a = nullptr;}private:int* _a;int _capacity;int _top;};
void Func(Stack stt) {//....
}
int main() {Stack st1;Func(st1);Stack st2(st1);return 0;
}

通过调试上述代码我们发现,它们_a的地址不同,但是_capacity、_top的值是相同的,成功完成了拷贝构造。
在这里插入图片描述
运行后的结果如下:
在这里插入图片描述

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

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

http://www.lryc.cn/news/212950.html

相关文章:

  • 【手机端远程连接服务器】安装和配置cpolar+JuiceSSH:实现手机端远程连接服务器
  • Jupyter Notebook的使用
  • vue 使用vue-office预览word、excel,pdf同理
  • 【Spring Boot 源码学习】RedisAutoConfiguration 详解
  • Linux中如何进行粘贴复制
  • 多输入多输出 | Matlab实现k-means-LSTM(k均值聚类结合长短期记忆神经网络)多输入多输出组合预测
  • 学习笔记3——JVM基础知识
  • 图像处理:图片二值化学习,以及代码中如何实现
  • 如果你点击RabbitMQ Service - start了,但http://localhost:15672/#/还是访问不了,那么请看这篇博客!
  • Shell 脚本学习 day01
  • esp32 rust linux
  • 一文了解Elasticsearch
  • 一篇文章认识【性能测试】
  • linux环境mysql安装配置踩坑
  • 相关性网络图 | 热图中添加显著性
  • cocosCreator 之 微信小游戏授权设置和调用wxAPI获取用户信息
  • element ui el-table表格纵向横向滚动条去除并隐藏空白占位列
  • 防止python进程重复执行
  • LV.12 D13 C工程与寄存器封装 学习笔记
  • Java SE 学习笔记(十九)—— XML、设计模式
  • grafana InfluxDB returned error: error reading influxDB 400错误解决
  • 【LeetCode:150. 逆波兰表达式求值 | 栈】
  • 什么是神经网络,它的原理是啥?(2)
  • leetcode做题笔记206. 反转链表
  • 2023/10/31 JAVA学习
  • SurfaceFliger绘制流程
  • 系统架构设计师-第14章-云原生架构设计理论与实践-
  • conda 实践
  • 行业追踪,2023-10-31
  • springboot 配置多个Redis数据源详解