【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++的拷贝构造函数,为什么需要拷贝构造函数,什么是拷贝构造函数,怎么使用拷贝构造函数,默认拷贝构造函数。

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

相关推荐
黑不溜秋的16 分钟前
C++ 语言特性29 - 协程介绍
开发语言·c++
一丝晨光21 分钟前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
￴ㅤ￴￴ㅤ9527超级帅35 分钟前
LeetCode hot100---二叉树专题(C++语言)
c++·算法·leetcode
_GR1 小时前
每日OJ题_牛客_牛牛冲钻五_模拟_C++_Java
java·数据结构·c++·算法·动态规划
Death2001 小时前
Qt 中的 QListWidget、QTreeWidget 和 QTableWidget:简化的数据展示控件
c语言·开发语言·c++·qt·c#
六点半8881 小时前
【C++】速通涉及 “vector” 的经典OJ编程题
开发语言·c++·算法·青少年编程·推荐算法
coduck_S12004zbj2 小时前
csp-j模拟五补题报告
c++·算法·图论
Death2002 小时前
Qt 3D、QtQuick、QtQuick 3D 和 QML 的关系
c语言·c++·qt·3d·c#
sukalot2 小时前
windows C++-windows C++-使用任务和 XML HTTP 请求进行连接(二)
c++·windows
qianbo_insist3 小时前
simple c++ 无锁队列
开发语言·c++