【C++】拷贝复制:拷贝构造函数的使用

欢迎来到CILMY23的博客

本篇主题为:拷贝复制:拷贝构造函数的使用

博客主页:CILMY23-CSDN博客

个人专栏:Python | C++ | C语言 | 数据结构与算法

感谢观看,支持的可以给个一键三连,点赞关注+收藏。


写在前头:

构造函数函数名和类名相同,而析构函数是在前面加个 ~ ,我们也总结了最好是全缺省的构造函数更实用,以及构造函数和析构函数的调用顺序(链接),并且默认成员函数和默认构造函数也存在区别:

  • 默认构造函数不传参就可以调用的函数,例如:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都算默认构造函数,而且默认构造函数只能有一个
  • 默认成员函数:用户没有显式实现,编译器会生成的成员函数 

本篇我们将介绍默认的成员函数其中特殊的拷贝构造函数


目录

一、什么是拷贝构造函数?

二、如何正确使用拷贝构造函数

2.1 拷贝构造函数的特点

2.2 拷贝构造函数的应用场景

2.3、无穷递归调用的源头--传值传参和传引用传参

2.3.1 传值传参

2.3.2 传引用传参 

2.3.3 使用 const 修饰 


一、什么是拷贝构造函数?

拷贝构造函数(有时又称为复制构造函数)是一种特殊的构造函数在对象创建时用于从同一类的现有对象中初始化新对象

也就是在创建对象时,创建一个与已存在对象一某一样的新对象。它只有单个形参该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。拷贝构造函数可以通过深拷贝或浅拷贝来创建新对象的副本,具体取决于类的设计和需求。

例如:假设我们仍然有一个简易的学生类

class Student
{
public:
	Student(const char* name, int age, const char* ID)
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}
	void ShowInfo()
	{
		cout << "学生姓名:" << _name << endl;
		cout << "学生年龄:" << _age << endl;
		cout << "学生学号:" << _ID << endl;
	}
private:
	char _name[20];
	int _age;
	char _ID[20];
};

int main()
{
	Student stu1("xiaohong",20,"20020221");
	stu1.ShowInfo();

	Student stu2(stu1); // 拷贝构造函数
	stu2.ShowInfo();
	return 0;
}

二、如何正确使用拷贝构造函数

2.1 拷贝构造函数的特点

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

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用(看第三大点,传值传参和传引用传参)
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。  默认的拷贝构造函数对象按内存存储按 字节序完成拷贝这种拷贝叫做浅拷贝,或者值拷贝。(深浅拷贝区别可以看链接)注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
  4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗? 当然像日期类这样的类是没必要的。但是对有申请空间资源的,还是需要自己实现的。

2.2 拷贝构造函数的应用场景

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

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

对第三点:我们使用日期类举例

//函数返回值为类类型对象
class Date
{
public:
	Date(int year, int minute, int day)
	{
		cout << "Date(int,int,int):" << endl;
	}

	Date(const Date& d)
	{
		cout << "Date(const Date& d):" << endl;
	}

	~Date()
	{
		cout << "~Date():" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

Date Test(Date d)
{
	Date temp(d); 
	return temp; //调用拷贝构造函数
}

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

	return 0;
}

2.3、无穷递归调用的源头--传值传参和传引用传参

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

第二点说不用引用,会导致无穷递归调用,在了解之前我们得清楚C++中传值传参传引用传参的区别。

2.3.1 传值传参

在C语言中,传值传参会完成一个拷贝,而C++中自定义类型的拷贝会调用对应的拷贝构造函数,这点在C语言中是没有的。C语言中结构体传值传参,也可以传,但是会在某些情况下会出问题。

也就是我们看以下这段代码:

class Student
{
public:
	Student(const char* name, int age, const char* ID)//有参的构造函数
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}

	Student(Student stu) //拷贝构造函数
	{
		strcpy(_name, stu._name);
		strcpy(_ID, stu._ID);
		_age = stu._age;
	}

private:
		char _name[20];
		int _age;
		char _ID[20];
};

int main()
{
	Student stu1("xiaoming",21,"2020066S16");
	Student stu2(stu1);

	return 0;
}

它的过程是这样的:

我们想将 stu1 拷贝给 stu2 ,但是在这之前对自定义类型的值调用对应的拷贝构造函数,于是对 stu1 对象进行拷贝,对自定义类型的拷贝会继续调用拷贝构造函数,将 stu1 将拷贝给 stu ,但是传值 stu1 前,又将对参数 stu1 拷贝,最终将形成一个无穷递归调用。为了解决这个问题,我们就用上了引用传参

2.3.2 传引用传参 

如果我们使用传引用传参,则可以将这个问题解决,

代码:

class Student
{
public:
	Student(const char* name, int age, const char* ID)//有参的构造函数
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}

	Student(Student& stu) //拷贝构造函数
	{
		strcpy(_name, stu._name);
		strcpy(_ID, stu._ID);
		_age = stu._age;
	}

private:
	char _name[20];
	int _age;
	char _ID[20];
};

int main()
{
	Student stu1("xiaoming", 21, "2020066S16");
	Student stu2(stu1);

	return 0;
}

过程: 

因为是引用,所以我们根本不需要调用对应的拷贝构造函数,从而解决无穷递归调用这个问题。而且为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用就尽量使用引用

2.3.3 使用 const 修饰 

Student(const Student& stu) //拷贝构造函数
{
	strcpy(_name, stu._name);
	strcpy(_ID, stu._ID);
	_age = stu._age;
}

对拷贝的对象来说,我们并不希望改变对象的值,所以我们会在引用前加一个const,以用来保证安全性。 


总结:

  • 所有的自定义类型,最后还是内置类型,它们类似一个套娃的形式
  • 析构函数并不是所有类都需要
  • 对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝,通常是对内置类型拷贝
  • 内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
  • 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以。一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
  • 拷贝构造函数也是构造函数,如果只写了拷贝构造函数,在调用默认构造函数时可以用default关键字强制编译器生成。例如:Student() = default;
  • C++中自定义类型的拷贝会调用对应的拷贝构造函数,这点在C语言中是没有的。

感谢各位同伴的支持,本期拷贝构造函数篇就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。  

  • 44
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
C++中,复制构造函数和拷贝构造函数是指同一个概念,用于创建一个对象的副本。它们被定义为类的特殊成员函数,用于将一个对象的值复制到另一个对象中。 复制构造函数/拷贝构造函数的语法如下: ```cpp ClassName(const ClassName& obj) { // 复制obj的成员变量到新对象中 } ``` 其中,`ClassName`是类的名称,`obj`是同类对象的引用,用于初始化新创建的对象。 在使用复制构造函数时,编译器会自动调用它来创建一个对象的副本。例如: ```cpp ClassName obj1; // 创建一个对象obj1 ClassName obj2(obj1); // 使用obj1调用复制构造函数创建obj2,obj2是obj1的副本 ``` 需要注意的是,如果没有显式定义复制构造函数/拷贝构造函数,编译器会为类提供一个默认的复制构造函数,该构造函数会逐个复制类的成员变量。但是如果类中有指针成员变量或资源管理等特殊情况,则需要自定义复制构造函数来确保正确地复制对象。 同时,复制构造函数/拷贝构造函数也可以通过赋值运算符重载来实现对象的复制。例如: ```cpp ClassName obj1; // 创建一个对象obj1 ClassName obj2 = obj1; // 使用赋值运算符重载实现对象的复制 ``` 这里的赋值运算符重载函数会被编译器解析为复制构造函数/拷贝构造函数的调用。 总结:复制构造函数/拷贝构造函数是用于创建对象的副本的特殊构造函数,它们采用同类对象的引用作为参数,并使用该对象的值来初始化新创建的对象。如果未显式定义复制构造函数/拷贝构造函数,编译器会提供一个默认的复制构造函数/拷贝构造函数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值