【C++ | 拷贝构造函数】一文了解C++的 拷贝(复制)构造函数

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-07 21:05:34

本文未经允许,不得转发!!!

目录

  • [🎄一、为什么需要 拷贝构造函数](#🎄一、为什么需要 拷贝构造函数)
  • [🎄二、什么是 拷贝构造函数](#🎄二、什么是 拷贝构造函数)
  • [🎄三、使用 拷贝构造函数](#🎄三、使用 拷贝构造函数)
  • [🎄四、默认的 拷贝构造函数](#🎄四、默认的 拷贝构造函数)
  • 🎄五、总结


🎄一、为什么需要 拷贝构造函数

如果程序中出现需要 拷贝构造函数 的代码,而又没有提供拷贝构造函数时,系统会提供一个默认的拷贝构造函数,该函数只会完成浅拷贝而不会进行深拷贝,这就是为什么需要 拷贝构造函数 的原因。

关于浅拷贝、深拷贝的知识,可以看这篇文章:C++入门知识-拷贝构造函数-浅拷贝、深拷贝

下面用例子说明浅拷贝可能产生的问题:

c 复制代码
// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 调用拷贝构造函数
	
	date_1.show();
	date_2.show();
	
	return 0;
}

上面代码由于没有进行深拷贝,导致 double free 了的错误,因为浅拷贝只复制了str的值,在两个对象销毁时都delete了。


🎄二、什么是 拷贝构造函数

拷贝构造函数,有些书也把其成为复制构造函数,其作用是将一个对象复制到新创建的对象中。

类的拷贝构造函数原型通常是这样的:类名(const 类名 &);。以CDate类为例,其拷贝构造函数如下:

cpp 复制代码
CDate(const CDate &);

拷贝构造函数的几个特点:

1、函数名和类名相同,因为它也是构造函数的一种;

2、第一个参数必须是一个自身类类型的引用,且其他参数都有默认值。

3、第一个参数必须是自身类类型的引用的原因:如果不上引用则需要拷贝它的实参,为了要拷贝实参,又需要调用拷贝构造函数,如此无限循环;

清楚了这些之后,我们修改一下上个小节的代码,添加一个拷贝构造函数:

cpp 复制代码
// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", m_year,m_mon,m_day);
	cout << "Calling Copy Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 调用date_2的拷贝构造函数
	
	date_1.show();
	date_2.show();
	
	return 0;
}

运行结果如下,添加拷贝构造函数后,运行不会报错,因为拷贝构造函数中重新new了内存:


🎄三、使用 拷贝构造函数

为了使用 拷贝构造函数,我们必须清楚 拷贝构造函数 在什么情况下会被调用,然后再根据自己设计的类是否需要深拷贝来决定怎样定义该类的 拷贝构造函数。

会调用拷贝构造函数的几种情况:

  1. 使用同类型的对象去初始化另一个对象时。
    如下代码,使用了 date_1 初始化 date_2:

    cpp 复制代码
    CDate date_1(2024,06,05);
    CDate date_2 = date_1;	// 调用date_2的拷贝构造函数
  2. 将一个对象作为实参传递给一个非引用类型的形参。

    cpp 复制代码
    void printDate(CDate date)
    {
    	date.show();
    }
    ...
    printDate(date_2);	// 实参传值到形参,调用拷贝构造函数
  3. 从一个返冋类型为非引用类型的函数返回一个对象。

    cpp 复制代码
    CDate g_date(2024,06,06);;
    CDate getDate()
    {
    	return g_date;	// 3、返回对象时,调用拷贝构造函数
    }

下面例子演示了调用拷贝构造函数的这三种场景:

cpp 复制代码
// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", m_year,m_mon,m_day);
	cout << "Calling Copy Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

void printDate(CDate date)
{
	date.show();
}

CDate g_date(2024,06,06);;
CDate getDate()
{
	return g_date;	// 3、返回对象时,调用拷贝构造函数
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 1、调用date_2的拷贝构造函数
	
	date_1.show();
	date_2.show();
	cout << endl;
	
	printDate(date_2);	// 2、实参传值到形参,调用拷贝构造函数
	cout << endl;
	
	getDate();
	cout << endl;
	
	return 0;
}

运行结果:


🎄四、默认的 拷贝构造函数

如果没有为一个类定义拷贝构造函数,则编译器会合成一个"默认拷贝构造函数"。

默认的拷贝构造函数会逐个复制非静态成员( 成员复制也称为浅复制)的值到正在创建的对象中。根据成员类型有下面几种情况:

1、如果成员是内置类型,则直接复制;

2、如果成员本身就是类对象,则将使用这个类的拷贝构造函数来复制类对象;

3、如果成员是数组,默认的拷贝构造函数会逐元素地拷贝一个数组类型的成员。


🎄五、总结

👉本文介绍C++的拷贝构造函数,为什么需要拷贝构造函数,什么是拷贝构造函数,怎么使用拷贝构造函数,默认拷贝构造函数。

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

相关推荐
CodeWithMe4 分钟前
【C/C++】C++中noexcept的妙用与性能提升
c语言·开发语言·c++
非著名架构师5 分钟前
C++跨平台开发实践:深入解析与常见问题处理指南
开发语言·c++
SuperCandyXu10 分钟前
leetcode0310. 最小高度树-medium
数据结构·c++·算法·leetcode
虾球xz31 分钟前
游戏引擎学习第264天:将按钮添加到分析器
c++·学习·游戏引擎
YKPG1 小时前
C++学习-入门到精通-【5】类模板array和vector、异常捕获
java·c++·学习
南玖yy1 小时前
内存安全革命:工具、AI 与政策驱动的 C 语言转型之路
c语言·开发语言·c++·人工智能·安全·c++23·c++基础语法
愚润求学2 小时前
【Linux】自定义shell的编写
linux·运维·服务器·开发语言·c++·笔记
m0_555762902 小时前
手势、鼠标滑动实现界面切换
c++·qt
小王努力学编程2 小时前
高并发内存池(二):项目的整体框架以及Thread_Cache的结构设计
开发语言·c++·学习·算法
old_power3 小时前
C++使用PoDoFo库处理PDF文件
c++·pdf