首页 > 其他分享 >拷贝构造函数

拷贝构造函数

时间:2025-01-13 23:29:38浏览次数:3  
标签:month year Date 拷贝 day 构造函数

文章目录


今天我们来学习拷贝构造函数

在这里插入图片描述

一、4. 拷贝构造函数

如果⼀个构造函数的第⼀个参数是自身类型的引用,且任何额外的参数都有默认值,则此叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。
它的形式是这样的:

#include<iostream>
using namespace std;
class Date
{ 
//拷贝构造函数		
Date(const Date & d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
private:
	int _year;
	int _month;
	int _day;
};

拷贝构造具有以下特点:

  1. 拷贝构造函数是构造函数的⼀个重载。

拷贝构造函数和普通构造函数一样,名字与类名相同,并且也是用于创建对象(用已存在的对象创建新对象)。它与其他构造函数的区别在于参数类型,它的参数是同类对象的引用。拷贝构造函数通过其特殊的参数类型(类对象的引用)与其他构造函数形成了重载关系。编译器可以根据调用时的参数情况(是普通参数还是类对象引用)来决定调用哪一个构造函数。

 Date(int year = 1, int month = 1, int day = 1)
{
	 _year = year;
	 _month = month;
	 _day = day;
 }
		
Date(const Date & d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

int main()
{
//既初始化了对象,又调用了拷贝构造
	Date d1();
	return 0;
}

  1. 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值。

Date &d 是为引用,目的是防止程序循环拷贝构造。如果为(Date d),则主程序Date d2(d1)时将d1拷贝赋值给了d,每次要调用烤贝构造函数之前要先传值传参,传值传参是一种烤贝,又形成一个新的拷贝构造,就形成了无穷递归。
const目的是为了保证被引用的对象不因拷贝的改变而改变。

#include<iostream>
using namespace std;
class Date
{ 

 public:
 
 Date(int year = 1, int month = 1, int day = 1)
{
	 _year = year;
	 _month = month;
	 _day = day;
}
//拷贝构造函数		
Date(const Date & d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 7, 5);
	Date d2(d1);
	d2.Print();
	return 0;
}

在这里插入图片描述


  1. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。

C++把类型分成内置类型(基本类型)和自定义类型
内置类型 :就是语言提供的原生数据类型,如:int/char/double/指针等。
自定义类型 :就是我们使用class/struct等关键字自己定义的类型。

#include<iostream>
using namespace std;
class Date
{
public:
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		cout << " const Date& d" << endl;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void Func1(Date d)
{
	
}

int main()
{
	Date d1;
	Func1(d1);
	return 0;
}


在这里插入图片描述
该例子说明了Func1函数调用了拷贝函数构造。
原因是Func1(Date d),Date d 调用了拷贝。


  1. 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。
#include<iostream>
using namespace std;
class Date
{

public:

	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}		
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(d1);
	d2.Print();
	return 0;
}

在这里插入图片描述


  1. Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源(malloc),生成了空间,浅拷贝会将地址也拷贝成一样的。堆空间释放两次,会报错。所以编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的拷贝构造会调用Stack的拷贝构造,也不需要我们显示实现MyQueue的拷贝构造。

这里还有一个小技巧,如果一个类显示实现了析构并释放资源,那么他就需要显示写拷贝构造(因为释放了资源就说明当初开了资源,如果不写拷贝构造,那么系统自动生成的拷贝构造会由于浅拷贝而出错),否则就不需要。

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}		
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	
	// st2(st1)
	Stack(const Stack& st)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		memcpy(_a, st._a, sizeof(STDataType) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(STDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
				sizeof(STDataType));
			if (tmp == NULL)
			{
				perror("realloc fail");
				return;
			}
			_a = tmp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;
	}

	void Pop()
	{
		_a[_top - 1] = -1;
		--_top;
	}

	int Top()
	{
		return _a[_top - 1];
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
};



  1. 传值返回,会产生一个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。但是如果返回对象是一个当前函数的局部域的局部对象,函数结束就销毁了,原来的那块空间释放了,那么使用引用返回是有问题的,这时的引|用相当于一个野引用,类似一个野指针一样。会非常危险。

传引用返回可以减少拷贝,但是一定要确保返回对象,在当前函数结束后还在,才能用引用返回。不能用局部变量当返回对象


感谢大家能看到这里,多多支持!

在这里插入图片描述

标签:month,year,Date,拷贝,day,构造函数
From: https://blog.csdn.net/q38491/article/details/145125111

相关文章

  • C++ —— 构造函数和析构函数
    C++——构造函数和析构函数引言构造函数析构函数注意事项引言构造函数和析构函数是class和C++的struct专属的功能(C的struct没有),用于管理对象的生命周期。构造函数:在创建对象时,自动的进行初始化工作。析构函数:在销毁对象前,自动的完成清理工作。构造函数访问权限......
  • 何时使用构造函数?
    在前端项目中,构造函数(constructor)是用于创建对象的一个特殊方法,通常在以下几种场景下使用:1.自定义类的实例化(类的构造函数)在ES6中,class提供了面向对象编程的方式。每个类都可以有一个构造函数,用来初始化类的实例。典型使用场景:定义一个对象模板,并在实例化时初始化对象的......
  • 【C++】构造函数与析构函数
    写在前面构造函数与析构函数都是属于类的默认成员函数!默认成员函数是程序猿不显示声明定义,编译器会中生成。构造函数和析构函数的知识需要建立在有初步类与对象的基础之上的,关于类与对象不才在前面笔记中有详细的介绍:点我跳转文章目录写在前面一、构造函数的特性1.1......
  • Effective C++读书笔记——item12(自定义拷贝构造函数和拷贝赋值运算符可能出现的问题
    1.拷贝函数相关背景及编译器行为在面向对象系统中,拷贝构造函数和拷贝赋值运算符统称为拷贝函数,若不自行声明,编译器会按需生成默认的拷贝函数,其会拷贝被拷贝对象的全部数据。但当自行声明拷贝函数后,编译器若发现实现存在错误,往往不会主动提示,比如在新增数据成员却未更新拷贝函......
  • ROBOCOPY Windows文件拷贝神器
    ROBOCOPY,即RobustFileCopy,是Windows操作系统中一个命令行实用程序,用于文件和目录的复制。它最初是作为XCOPY的替代品开发的,提供了更多的功能和更好的可靠性。ROBOCOPY能够处理大规模的数据复制任务,并且在遇到错误时具备重试机制,可以跳过不可用的文件,继续复制其余的文件,这使得它......
  • 请说说原型对象、构造函数、实例对象三者之间的关系?
    在JavaScript中,原型对象(prototype)、构造函数(constructor)和实例对象(instance)之间存在着紧密的关系。这种关系构成了JavaScript中面向对象编程(OOP)的基础。下面将详细解释这三者之间的关系:构造函数(Constructor):构造函数是一个特殊的函数,用于创建和初始化由该构造函数构造的一个新......
  • 在TypeScript中如何从子类调用基类构造函数?
    在TypeScript中,你可以使用super关键字来从子类调用基类(也就是父类)的构造函数。super关键字在子类的构造函数内部使用,而且必须在使用this关键字之前调用。下面是一个简单的例子:classBaseClass{constructor(publicname:string){console.log("BaseClassconstru......
  • 移动构造函数详解
    概念移动构造函数是c++11引入的特性,用于将资源从一个对象高效的转移到另一个对象,避免不必要的拷贝动作。移动构造函数的基本语法如下:classMyClass{private:std::vector<int>data;public://移动构造函数MyClass(MyClass&&other)noexcept;//注意移动构......
  • python 赋值、深拷贝浅拷贝及切片使用
    赋值、深浅拷贝先复习一下赋值与深浅拷贝i=[1,2,1,3,[1,2]]j=i#赋值k=i.copy()#浅拷贝m=copy.deepcopy(i)#深拷贝#赋值,二者物理地址相同,一方变化另一方同步变化j.pop(0)print(i,j)[2,1,3,[1,2]][2,1,3,[1,2]]#取浅拷贝,二者物理......
  • Object.assign()是浅拷贝还是深拷贝?
    Object.assign()在JavaScript中是执行浅拷贝(shallowcopy)的。这意味着,它只复制对象的顶层属性和值。如果对象的属性值是一个引用类型(例如,数组或另一个对象),Object.assign()不会复制这个引用类型的实际内容,而是复制这个引用本身。因此,原对象和新对象会共享这个引用,对一个对象的......