首页 > 编程语言 >c++:类和对象中:拷贝构造和赋值运算符重载详解

c++:类和对象中:拷贝构造和赋值运算符重载详解

时间:2024-03-13 20:33:33浏览次数:26  
标签:year c++ month 运算符 Date ._ 重载 拷贝 day

c++:类和对象

构造函数和析构函数详解

`

文章目录


前言

拷贝构造,顾名思义就是复制.我们把一个已经创建自定义对象初始化创建新对象.
赋值运算符重载是把一个对象赋值给另一个对象,二者都是已创建的.


一、拷贝构造

怎么写拷贝构造

#include<iostream>
using namespace std;
class Date
{
public:
	//构造函数,对自定义对象进行初始化
	Date(int year = 2004, int month = 4, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	friend ostream& operator<<(ostream & out, Date & d);
private:
	int _year;
	int _month;
	int _day;
};
//这个是<<的运算符重载
ostream& operator<<(ostream& out, Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

int main()
{
	Date d1(2024,3,13);
	Date d2 = d1;
	cout << "d1:" << d1;
	cout << "d2:" << d2;

	return 0;
}

在这里插入图片描述

1.拷贝构造也是构造函数的一种,构造函数没有值.所以拷贝构造也没有返回值**

2.拷贝构造只有一个形参,正常这个形参是自定义类型对象的引用.

比如在Date这个类里面就是Date&.
为什么一定要是引用呢?我们都知道,函数的参数是一个形参,我们调用函数传过去的才是实参.而形参是实参的一个拷贝,我们要传值过去就必须创建一个形参.
我们传自定义类型Date的值时,因为要拷贝一个新对象,所以就要调用Date类型的拷贝构造.要是拷贝构造里面的参数也是Date的话,就会继续调用它的拷贝构造.如此形成无限递归.
在这里插入图片描述
传值调用拷贝构造编译器会自动报错

3. 如果我们没有显示写拷贝构造,编译器会自己生成.但是这种只是浅拷贝,在某些情况下面有危险

在这里插入图片描述

在这里插入图片描述

编译器默认的拷贝构造函数是按字节进行拷贝,在我们上面的日期类适用.因为我们没有动态开辟空间.

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		//动态开辟空间
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc fail");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

上面的代码在程序运行会崩溃

这是为什么?编译器不是会按字节去对内置类型进行拷贝吗?
在这里插入图片描述
结果调试,发现问题出现在free上,free被调用了两次.同一块空间被删除了两次.这才是导致问题出现的关键
第一次free
在这里插入图片描述

第二次free
在这里插入图片描述
编译器默认写的是浅拷贝,也叫值拷贝.它只会按字节去拷贝.在我们动态开辟空间时,浅拷贝不会开辟空间.这样子会导致两个对象的_array指针指向同一块空间.
自定义类型结束后会调用它的析构函数.因为有两个对象,所以析构两次,free两次.

	Stack(Stack& st)
	{
		DataType* tmp = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (nullptr == tmp)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(tmp, st._array, st._capacity* sizeof(DataType));
		_array = tmp;
		_size = st._size;
		_capacity = st._capacity;
	}

上面的类里面加上拷贝构造函数就不会报错,能够正常运行.这个也就是深拷贝.

二、赋值运算符重载


代码实现

`#include<iostream>
using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//构造函数,对自定义对象进行初始化
	Date(int year = 2004, int month = 4, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//友元的声明
	friend ostream& operator<<(ostream& out, Date& d);

	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
};
	运算符重载
ostream& operator<<(ostream& out, Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

int main()
{
	Date d1(2024,3,28);
	Date d2;
	cout << "d1:" << d1;
	cout << "d2:" << d2;

	d2 = d1;
	cout << "d1:" << d1;
	cout << "d2:" << d2;
	return 0;
}`

赋值运算符重载和拷贝构造类似,本质还是对对象的拷贝.只不过拷贝构造是在对象初始化时运用的,赋值运算符是对已经存在的对象赋值
赋值运算符重载编译器默认生成的跟拷贝构造一样,都是浅拷贝
在这里插入图片描述
书写格式

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

1.operator是对内置操作符进行重载,如operator+
2.重载操作符必须有一个类型参数,最好用const修饰一下 , const Date&
3.形参总是比操作数少一(a=b有两个操作数),因为成员函数的第一个参数为this指针
4…
:: sizeof ?: . 注意以上5个运算符不能重载.面试/笔试可能会考
*

为什么返回值是Date&呢?
大家可能对这个问题比较好奇.
首先,我们的语言支持连等,我们可以同时给a,b,c三个变量赋值.

int main()
{
	int a,b,c;
	a = b = c = 10;
	return 0;
}

当我们把上面赋值运算符重载的代码改成无返回值时,我们的连续赋值就会报错.
在底层实现d3=d2=d1时,会调用赋值运算符重载这个函数,因为赋值是从右往左过去,故将d1赋值给d2,如果没有返回值的话,就没有人来给d3赋值.程序就会崩溃.所以返回值必须是左操作数的引用.

	void operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	
	}

在这里插入图片描述

总结

拷贝构造和赋值运算符是同一类成员函数.不主动写编译器会默认生成.但是生成的这个只是浅拷贝,在日期类这种没有分配资源的类可以用
但是在有开辟空间的类上不适用,要自己写.

标签:year,c++,month,运算符,Date,._,重载,拷贝,day
From: https://blog.csdn.net/2301_76886465/article/details/136679522

相关文章

  • L1-011 A-B(C和C++)
    题目:本题要求你计算A−B。不过麻烦的是,A和B都是字符串——即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串A−B。输入格式:输入在2行中先后给出字符串A和B。两字符串的长度都不超过104,并且保证每个字符串都是由可见的ASCII码和空白字符组成,最后以换行......
  • 【C++】【OpenCV-4.9.0】视频写入(VideoWriter,借助samples中的代码示例来进行学习)
    借助官方离线文档中的samples来理解VideoWriter文档位置:samples/cpp/tutorial_code/videoio/video-write/video-write.cpp注:需要提前下载openh264-1.8.0-win64.dll,然后放在Release文件夹下,否则无法正确对输出文件进行编码从而运行失败1#include<iostream>2#include......
  • C++ | 二分(重点二分答案)
    常见的二分类型:1.整数二分2.小数二分(相对较少)3.二分答案(最常见)二分最直接的思想就是用O(logn)的时间较快的在数组中找见想要找的边界数,枚举时间较慢O(n)。 小数二分与整数二分的思想相一致,就不在赘述了。☆ 理解二分一定要理解的问题是答案取得是区间的右边界,则返回......
  • 04_C++字符串_vector使用
    1.初始化vector vector<int>v1;默认初始化vector<int>v2(10);10个int类型的元素,初始化值为-1vector<string>v3{"a","bb","ccc"};列表初始化,包括三个元素2.向vector添加元素#include<iostream>#include<string>#include<vector>......
  • windbg 调试 c++ std::exception
    由于c++std::exception在windbg里面调用堆栈显示不正确,可以通过加载了系统pdb和软件pdb后,!analyze-v可以分析出来,所有需要配置系统pdb。1、把exe,pdb放到同一个目录下。2、加载dmp文件(拖动dmp文件到windbg)。3、设置pdb路径:File->SymbolFilePath,比如D:\Desktop\3dLayer,记得加......
  • C++纯虚函数和抽象类
    在C++中,可以将虚函数声明为纯虚函数,语法格式为:virtual返回值类型函数名(函数参数)=0;纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。最后的=0并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。包含纯虚函数的类称......
  • 突破编程_C++_C++11新特性(模板的改进与细节)
    1模板右尖括号的改进在C++11之前,模板的解析和实例化过程中,右尖括号>的处理有时会导致一些意外的结果,特别是在嵌套模板或模板模板参数中。这是因为C++编译器通常会试图“查看前方”来确定何时结束模板参数的列表,这有时会导致解析错误。C++11对模板的右尖括号处理进......
  • 【C++】thread 头文件无法正常使用问题
    问题当我使用MinGWGCC,在windows上编写C++程序的时候,使用thread类会无法编译,有如下错误:我使用的是c++17这个问题原因是MinGWGCC当前仍缺少标准C++11及以上版本线程类的实现。解决方案Stackoverflow上的类似问题:传送门1.下载源文件:源文件外网可能比较慢,提供C......
  • C++多态和虚函数
    C++多态和虚函数#include<iostream>usingnamespacestd;//基类PeopleclassPeople{public:People(char*name,intage);voiddisplay();protected:char*m_name;intm_age;};People::People(char*name,intage):m_name(name),m_age(age){}......
  • C++初阶:1_C++入门
    C++入门零.本节知识点安排目的C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。熟悉C语言之后,对C++学习有一定的帮助,本章节主要目标:补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,比如:作用域方面、IO方面、函数......