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

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

相关推荐
CYBEREXP200832 分钟前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama1 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou2 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生972 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns2 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns2 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库2 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构
_小柏_3 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++