「C++ 类和对象篇 7」拷贝构造函数

目录

一、 概念

1. 拷贝构造函数是什么?

2. 为什么要有拷贝构造函数?

3. 怎么用拷贝构造函数?

3.1 创建拷贝构造函数

3.2 调用拷贝构造函数

二、特征

三、合成拷贝构造函数

1. 是什么?

2. “双重删除”问题

3. 什么时候需要显示的写拷贝构造函数?

四、在拷贝构造函数中实现深拷贝

1. 自己开辟一个新空间,然后将内容拷贝到新空间。

2. 借助构造函数来实现深拷贝。

【总结】

【源代码】 


一、 概念

1. 拷贝构造函数是什么?

       拷贝构造函数是一个特殊的构造函数,也是用来初始化对象的,不过它是用已经存在的对象来初始化同类对象。

2. 为什么要有拷贝构造函数?

       在创建新对象时,可否用已经存在的同类对象来初始化这个新对象呢?能否快速拷贝出一个对象的副本呢?

       为解决以上问题,C++中引入了拷贝构造函数:拷贝构造函数用于实现对象的复制和初始化。

3. 怎么用拷贝构造函数?

3.1 创建拷贝构造函数

       和构造函数一样,函数名和类名相同,且没有返回值,但拷贝构造函数的参数是当前类类型对象的引用或是指向当前类类型对象的指针。不能用当前类类型对象作为参数,这样会引发无穷递归问题。

class Date
{
public:
	Date(int year = 2022, int month = 10, 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;
	}
	Date(const Date* d)
	{
		_year = d->_year;
		_month = d->_month;
		_day = d->_day;
	}
    // 拷贝构造函数可以有多个参数,但一般只以一个对象为副本进行拷贝,所以只写一个参数。
    Date(const Date& d1,const Date& d2,const Date* d3)
	{
		_year = d1._year;
		_month = d2._month;
		_day = d3->_day;
	}
private:
	int _year;
	int _month;
	int _day;
};

        所以拷贝构造函数的参数必须是当前类类型对象的引用或是指向本类类型的指针,不然会引起以下拷贝构造函数传值传参带来的无穷递归问题

        拷贝构造函数如使用传值传参的方式,会引发无穷递归调用编译器会直接报错。因为使用传值传参时,编译器要调用拷贝构造函数用实参初始化形参,使用拷贝构造函数就必须得先传参,又会调用拷贝构造函数,这样就造成了造成无穷递归。


        在传引用传参或指针传参时,对于像拷贝构造函数的形参这类不需要修改的参数,建议加上一个const。这样做即能避免误操作导致实参被修改,也能误操作时给我们一个提示,让我们快速定位错误。

eg. 加上const后实参不能被修改。

3.2 调用拷贝构造函数

a. 参数是 对象的引用 的拷贝构造函数

int main()
{
	Date a(1,1,1);
	Date b(a);
	return 0;
}


b. 参数是 指向对象的指针 的拷贝构造函数

int main()
{
	Date c(3, 3, 3);
	Date d(&c);
	return 0;
}


c. 多个参数的拷贝构造函数

拷贝构造函数可以有多个参数,但一般只以一个对象为副本进行拷贝只写一个参数。

int main()
{
	Date a(1, 1, 1);
	Date b(2, 2, 2);
	Date c(3, 3, 3);
	Date d(a, b, &c);
	return 0;
}


二、特征

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

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

2. 拷贝构造函数的参数必须是本类类型的引用或是指向本类类型的指针。使用传值传参方式编译器会直接报错,因为会引发无穷递归调用。

3. 拷贝构造函数可以有多个参数,但一般只以一个对象为副本进行拷贝,所以只写一个参数。

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


三、合成拷贝构造函数

1. 是什么?

若未显式定义,编译器会生成默认的拷贝构造函数,叫做合成拷贝构造函数。

注意:在编译器生成的默认拷贝构造函数中,内置类型成员是按照字节方式直接拷贝的,是浅拷贝,而自定义类型成员是调用其拷贝构造函数完成拷贝的。

2. “双重删除”问题

        编译器生成的默认拷贝构造函数只能进行浅拷贝,无法对申请的资源(如动态开辟的空间)进行拷贝,看下面的例子:


        在以上例子中我们发现,默认的拷贝构造函数会令拷贝的类和被拷贝的类中的指针变量指向同一块空间,这样会造成同一块空间被析构函数析构两次,这通常被称为“双重删除”或“重复删除”,这是一个严重的问题,会导致程序崩溃。这个时候需要显示的写一个构造函数,并在里面完成深拷贝。

3. 什么时候需要显示的写拷贝构造函数?

        编译器生成的默认拷贝构造函数只能进行浅拷贝,无法对申请的资源(如动态开辟的空间)进行拷贝。

        所以类中如果没有涉及资源申请时,拷贝构造函数是否写都可以。一旦涉及到资源申请时,一定要显示的写拷贝构造函数,否则就是浅拷贝,可能导致“双重删除”问题。


四、在拷贝构造函数中实现深拷贝

1. 自己开辟一个新空间,然后将内容拷贝到新空间

2. 借助构造函数来实现深拷贝。

       在构造函数中自然要动态成员变量开辟空间,所以在拷贝构造函数中可以使用构造函数创建一个临时对象,然后交换对象和临时对象的动态成员


       但这有个小问题,就是当前对象未初始化,直接交换数值可能会导致程序崩溃,所以加上初始化列表,在交换数值前先初始化。

最后再给new的失败加一个提示或抛异常。

想深入了解C/C++中深浅拷贝问题的同学,不妨看看博主的这篇文章: 「C/C++ 01」 深拷贝和浅拷贝


【总结】


【源代码】 

//class A
//{
//public:
//	int a;
//	A(int _a) { a = _a; }
//};
//class Date
//{
//public:
//	Date(int year = 2022, int month = 10, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	//Date(Date d) // 错误写法:编译报错,会引发无穷递归
//	// 正确写法,拷贝构造函数的参数必须是本类类型的引用或是指向本类类型的指针。
//	Date(const A& a)
//	{
//		_year = a.a;
//		_month = a.a;
//		_day = a.a;
//	}
//	Date(const Date& d1)
//	{
//		_year = d1._year;
//		_month = d1._month;
//		_day = d1._day;
//	}
//	Date(Date& d1, Date& d2, Date* d3)
//	{
//		_year = d1._year;
//		_month = d2._month;
//		_day = d3->_day;
//	}
//	Date(const Date* d)
//	{
//		_year = d->_year;
//		_month = d->_month;
//		_day = d->_day;
//	}
//
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//
//int main()
//{
//	A a(1);
//	Date c(3, 3, 3);
//	Date d(&c);
//	Date e(a);
//	return 0;
//}

#include<iostream>
using namespace std;


class stack
{
public:
	int* _arr;
	// 构造函数
	stack(int* arr = nullptr)
	{
		int len = sizeof(arr) / sizeof(arr[0]);
		_arr = new int[len];

		copy(arr, arr + len, _arr);
	}
	~stack()
	{
		cout << "删除地址为:" << _arr << "的数组。" << endl;
		delete(_arr);
	}
	// 1. 自己开辟一个新空间,然后将内容拷贝到新空间。
	stack(const stack &s)
	{
		//开辟新空间
		int len = sizeof(s._arr) / sizeof(s._arr[0]);
		_arr = new int[len];

		//拷贝内容
		copy(s._arr, s._arr + len, _arr);
	}

	 2. 借助构造函数创建中间对象来实现深拷贝。
	//stack(const stack& s)
	//{
	//	//调用构造函数创建中间对象(系统会开辟好空间)
	//	stack tmp(s._arr);
	//	swap(_arr, tmp._arr);
	//}
	 但这有个小问题,就是当前对象未初始化,
	 直接交换数值可能会导致程序崩溃,所以加上初始化列表,在交换数值前先初始化。
	//stack(const stack& s)
	//	:_arr(nullptr)
	//{
	//	//调用构造函数创建中间对象
	//	stack tmp(s._arr);
	//	swap(_arr, tmp._arr);
	//}

	stack& operator= (const stack& s)
	{
		// 自己给自己赋值时无需开辟新空间。
		if (this != &s)
		{
			//调用构造函数创建中间对象(系统会开辟好空间)
			stack tmp(s._arr);
			swap(_arr, tmp._arr);
		}
		return *this;
	}
};

int main()
{
	int* arr = new int[5];
	arr[0] = 0;
	stack st1(arr);
	stack st2 = st1;
	cout << "原数组的地址 :" << arr << endl \
		 << "st1数组的地址:" << st1._arr << endl \
		 << "st2数组的地址:" << st2._arr << endl;
	return 0;
}


------------------------END-------------------------

才疏学浅,谬误难免,欢迎各位批评指正。

内容概要:本文档详细介绍了在三台CentOS 7服务器(IP地址分别为192.168.0.157、192.168.0.158和192.168.0.159)上安装和配置Hadoop、Flink及其他大数据组件(如Hive、MySQL、Sqoop、Kafka、Zookeeper、HBase、Spark、Scala)的具体步骤。首先,文档说明了环境准备,包括配置主机名映射、SSH免密登录、JDK安装等。接着,详细描述了Hadoop集群的安装配置,包括SSH免密登录、JDK配置、Hadoop环境变量设置、HDFS和YARN配置文件修改、集群启动与测试。随后,依次介绍了MySQL、Hive、Sqoop、Kafka、Zookeeper、HBase、Spark、Scala和Flink的安装配置过程,包括解压、环境变量配置、配置文件修改、服务启动等关键步骤。最后,文档提供了每个组件的基本测试方法,确保安装成功。 适合人群:具备一定Linux基础和大数据组件基础知识的运维人员、大数据开发工程师以及系统管理员。 使用场景及目标:①为大数据平台建提供详细的安装指南,确保各组件能够顺利安装和配置;②帮助技术人员快速掌握Hadoop、Flink等大数据组件的安装与配置,提升工作效率;③适用于企业级大数据平台的建与维护,确保集群稳定运行。 其他说明:本文档不仅提供了详细的安装步骤,还涵盖了常见的配置项解释和故障排查建议。建议读者在安装过程中仔细阅读每一步骤,并根据实际情况调整配置参数。此外,文档中的命令和配置文件路径均为示例,实际操作时需根据具体环境进行适当修改。
在无线通信领域,天线阵列设计对于信号传播方向和覆盖范围的优化至关重要。本题要求设计一个广播电台的天线布局,形成特定的水平面波瓣图,即在东北方向实现最大辐射强度,在正东到正北的90°范围内辐射衰减最小且无零点;而在其余270°范围内允许出现零点,且正西和西南方向必须为零。为此,设计了一个由4个铅垂铁塔组成的阵列,各铁塔上的电流幅度相等,相位关系可自由调整,几何布置和间距不受限制。设计过程如下: 第一步:构建初级波瓣图 选取南北方向上的两个点源,间距为0.2λ(λ为电磁波波长),形成一个端射阵。通过调整相位差,使正南方向的辐射为零,计算得到初始相位差δ=252°。为了满足西南方向零辐射的要求,整体相位再偏移45°,得到初级波瓣图的表达式为E1=cos(36°cos(φ+45°)+126°)。 第二步:构建次级波瓣图 再选取一个点源位于正北方向,另一个点源位于西南方向,间距为0.4λ。调整相位差使西南方向的辐射为零,计算得到相位差δ=280°。同样整体偏移45°,得到次级波瓣图的表达式为E2=cos(72°cos(φ+45°)+140°)。 最终组合: 将初级波瓣图E1和次级波瓣图E2相乘,得到总阵的波瓣图E=E1×E2=cos(36°cos(φ+45°)+126°)×cos(72°cos(φ+45°)+140°)。通过编程实现计算并绘制波瓣图,可以看到三个阶段的波瓣图分别对应初级波瓣、次级波瓣和总波瓣,最终得到满足广播电台需求的总波瓣图。实验代码使用MATLAB编写,利用polar函数在极坐标下绘制波瓣图,并通过subplot分块显示不同阶段的波瓣图。这种设计方法体现了天线阵列设计的基本原理,即通过调整天线间的相对位置和相位关系,控制电磁波的辐射方向和强度,以满足特定的覆盖需求。这种设计在雷达、卫星通信和移动通信基站等无线通信系统中得到了广泛应用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烛火萤辉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值