【c++】类和对象3

news2024/9/23 23:26:26

拷贝构造函数

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

  1. 拷贝构造函数是构造函数的一个重载形式。
    函数名相同,参数不同

先定义一个类

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, 1, 28);
	Date d2(d1);

	d1.Print();
	d2.Print();

	return 0;
}

使用拷贝构造函数将d1的值拷给d2.
在这里插入图片描述


为了更好的理解,我们可以先定义两个函数

void func1(Date d)
{

}

void func2(Date& rd)
{

}
int main()
{
	Date d1(2024, 1, 28);
	
	func1(d1);
	func2(d1);

	return 0;
}

在调试过程中我们发现调用func1的时候,他会调用类的拷贝构造函数,如果调用func2函数的话,我们发现他不会去调用拷贝构造函数,而C++规定自定义的类型都会调用拷贝构造
在这里插入图片描述


这里有两个问题?
1.为什么构造拷贝函数要用传引用?
上面的例子也说明了,直接传类对象的话,他会自动的去递归找类对象的拷贝构造函数,如果拷贝构造函数的参数也是类对象的话,他又会去递归找拷贝构造函数,形成无穷递归.而传引用实际就是他的拷贝构造函数.
2.为什么拷贝构造函数的参数要加const?
因为我们可能会出现以下情况

	Date( Date& d)//拷贝构造函数
	{
		d._year = this->_year;
        d._month = this->_month;
        d._day = this->_day;





		/* _year = d._year;
		_month = d._month;
		_day = d._day;*/
	}

如果出现颠倒,编译器也不会报错
在这里插入图片描述
本来要把d2修改成d1,但是搞反,d1就会被修改成d2的值.

  1. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

  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(2024, 1, 28);
	Date d2(d1);

	d1.Print();
	d2.Print();

	return 0;
}

我们删除掉拷贝构造函数,看编译器会不会自动生成
在这里插入图片描述
编译器的确在我们没有定义拷贝构造函数会自动生成来默认拷贝构造函数进行拷贝.
而自定义类型需要调用他自己的默认拷贝函数.
因为自定义类型中可能会设计到申请内存,不能使用浅拷贝(单单只是值的拷贝)
在这里插入图片描述

  1. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
#include<iostream>
#include <stdlib.h>
using namespace std;
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
	
			_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_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;
}

在这里插入图片描述
在这里插入图片描述
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。


运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:
1不能通过连接其他符号来创建新的操作符:比如operator@
2重载操作符必须有一个类类型参数
3用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
4作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5 .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出
现。
这里有几个问题?
1.运算符重载函数可以定义在全局吗?
如果定义在全局,编译器会在类中找不到,由于他是默认成员函数,编译器会自己生成一个,会出现冲突,和全局的.原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
2.赋值运算符重载,检测是否自己给自己赋值返回.*this :要复合连续赋值的含义.
3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。这里的拷贝还是属于浅拷贝,如果类成员变量中有栈类型,赋值就会有问题,两个指针指向同一块空间.析构两个类对象会出现问题.
4.为了防止自己给自己赋值的情况
修改前:

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




}

修改后:

Date& Date:: operator=(const Date& d)
{
	if (this != &d)//这里是地址
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;




}

5.为什么有些类成员函数返回时用传引用返回,有些是传值返回?

Date& Date::operator++()//前置++
{
	*this = *this + 1;
	return *this;




}

Date Date:: operator++(int)//后置++
{

	Date tmp = *this;
	*this=*this+ 1;
	return tmp;

}

因为一个是临时变量,出作用域要被销毁,所以不能通过引用返回,这个之前讲过在引用那节.
6.后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递

日期类的实现

date.cpp

#include "date.h"
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

}
void Date:: print()
{
	cout << _year << "." << _month << "." << _day<<endl;
}
Date::Date(const Date& d)
{
	
	_year = d._year;
	_month = d._month;
	_day = d._day;



}
Date::~Date()
{
	
	_year = 0;
	_month = 0;
	_day = 0;


}
int Date:: GetMonthDay(int year, int month)
{
	int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0))
	{
		return 29;
	}
	return arr[month];

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




}
Date & Date::operator+=(int day)
{
	_day+= day;
	while (_day > GetMonthDay(_year,_month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_month = 0;
		}



	}
	return *this;




}
Date Date:: operator+(int day)
{
	Date tmp = *this;
	tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	{
		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		tmp._month++;
		if (tmp._month == 13)
		{
			tmp._month = 0;
		}



	}
	return tmp;
	




}
Date& Date:: operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
        _day += GetMonthDay(_year, _month);




	}
	return *this;



}
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= (day);
	return tmp;


}
bool Date:: operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	else
	{
		if (_year == d._year)
		{
			if (_month > d._month)
			{
				return true;
			}
			else
			{
				if (_month == d._month)
				{
					if (_day > d._day)
						return true;
				}
			}
		}




	}
	return false;


}
bool Date::operator==(const Date& d)
{
	return (_year == d._year) && (_month == d._month) &&( _day == d._day);





}
bool Date:: operator >= (const Date& d)
{
	return   *this > (d) || *this == (d);


}

bool Date:: operator < (const Date& d)
{
	return !(*this >= (d));


}
bool Date::operator <= (const Date& d)
{

	return  !(*this>(d));

}
bool Date:: operator != (const Date& d)
{
	return  !(*this==(d));

}

Date& Date::operator++()
{
	*this = *this + 1;
	return *this;




}

Date Date:: operator++(int)
{

	Date tmp = *this;
	*this=*this+ 1;
	return tmp;

}

Date Date::operator--(int)
{
	Date tmp =*this;
	*this=*this-1;
	return tmp;


}
Date& Date:: operator--()
{
	*this = *this - 1;
	return *this;


}



int Date::operator-(const Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (min > (max))
	{
		max = d;
		min = *this;
		flag = -1;

	}
	int n = 0;
	while (max != min)
	{
		n++;
		min++;




	}
	return flag*n;





}

date.h

#pragma once
#include<iostream>
using namespace std;
class Date

{

public:

	// 获取某年某月的天数

	int GetMonthDay(int year, int month);



	// 全缺省的构造函数

	Date(int year = 1900, int month = 1, int day = 1);



	// 拷贝构造函数

  // d2(d1)

	Date(const Date& d);



	// 赋值运算符重载

  // d2 = d3 -> d2.operator=(&d2, d3)

	Date& operator=(const Date& d);



	// 析构函数

	~Date();



	// 日期+=天数

	Date& operator+=(int day);



	// 日期+天数

	Date operator+(int day);



	// 日期-天数

	Date operator-(int day);



	// 日期-=天数

	Date& operator-=(int day);



	// 前置++

	Date& operator++();



	// 后置++

	Date operator++(int);



	// 后置--

	Date operator--(int);



	// 前置--

	Date& operator--();



	// >运算符重载

	bool operator>(const Date& d);



	// ==运算符重载

	bool operator==(const Date& d);



	// >=运算符重载

	bool operator >= (const Date& d);



	// <运算符重载

	bool operator < (const Date& d);



	// <=运算符重载

	bool operator <= (const Date& d);



	// !=运算符重载

	bool operator != (const Date& d);



	// 日期-日期 返回天数

	int operator-(const Date& d);

	void print();
	

private:

	int _year;

	int _month;

	int _day;

};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1420970.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【pytest系列】- parametrize参数化

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Springboot校验注解

Spring Boot 提供了一组基于 Hibernate Validator 的校验注解&#xff0c;用于验证请求参数、实体对象等数据的合法性。下面是一些常用的 Spring Boot 校验注解及其功能&#xff1a; 导入依赖 <dependency><groupId>org.springframework.boot</groupId><…

正则表达式补充以及sed awk

正则表达式&#xff1a; 下划线算 在单词里面 解释一下过程&#xff1a; 在第二行hello world当中&#xff0c;hello中的h 与后面第一个h相匹配&#xff0c;所以hello中的ello可以和abcde匹配 在world中&#xff0c;w先匹配h匹配不上&#xff0c;则在看0&#xff0c;r&#…

代码随想录算法训练营第二十一天 |530.二叉搜索树的最小绝对差,501.二叉搜索树中的众数,236.二叉树的最近公共祖先(待补充)

530.二叉搜索树的最小绝对差 1、题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 2、文章讲解&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 3、题目&#xff1a; 给你一棵所有节点为非…

Docker基于Dockerfile多级构建LNMP,实现缩小镜像体积

目录 实验准备&#xff1a; 1.创建nginx镜像 2.创建mysql镜像 3.创建php镜像 4.完成对接 创建网络 创建nginx容器 创建mysql容器 创建WordPress库 创建php容器 5.登录验证 6.镜像体积是不是越小越好&#xff1f;为什么要缩减镜像体积&#xff1f; 7.缩小镜像体积的…

防御保护 笔记整理

一、ASPF--- 针对应用层的包过滤 ASPF --- 针对应用层的包过滤 --- 用来抓取多通道协议中协商端口的关键数据包&#xff0c;之后&#xff0c;将端 口算出&#xff0c;将结果记录在sever-map表中&#xff0c;相当于开辟了一条隐形的通道。 FTP --- 文件传输协议 FTP协议是一个典…

数学算法知识编程

&#xff08;1&#xff09;辗转相除法求最大公约数&#xff08;gcd&#xff09; 辗转相除法&#xff0c; 又名欧几里德算法&#xff08;Euclidean algorithm&#xff09;&#xff0c;是求最大公约数的一种方法。它的具体做法是&#xff1a;用较小数除较大数&#xff0c;再用出现…

I.MX6ULL_Linux_驱动篇(53)linux USB驱动

I.MX6ULL USB 接口简介 I.MX6ULL 内部集成了两个独立的 USB 控制器&#xff0c;这两个 USB 控制器都支持 OTG 功能。I.MX6ULL 内部 USB 控制器特性如下&#xff1a; ①、有两个 USB2.0 控制器内核分别为 Core0 和 Core1&#xff0c;这两个 Core 分别连接到 OTG1 和OTG2。 ②、…

基于STM32的CAN通信协议选择与实现

基于STM32的控制器区域网络&#xff08;CAN&#xff09;通信协议是一种常见的实时数据通信方案&#xff0c;适用于需要高速、可靠通信的应用场景&#xff0c;比如汽车网络、工业控制系统等。在这里&#xff0c;我们将详细介绍基于STM32的CAN通信协议的选择与实现。 ✅作者简介&…

使用Win32API实现贪吃蛇小游戏

目录 C语言贪吃蛇项目 基本功能 需要的基础内容 Win32API 介绍 控制台程序部分指令 设置控制台窗口的长宽 设置控制台的名字 控制台在屏幕上的坐标位置结构体COORD 检索指定标准设备的句柄&#xff08;标准输入、标准输出或标准错误&#xff09; 光标信息结构体类型CONSOLE_CUR…

excel给数据库初始化/旧数据处理(自动sql拼装)

思路&#xff1a; 首先导出数据到excel编写单条数据操作的sql利用excel CONCATENATE 函数自动生成&#xff0c;每一行数据的操作sql 小技巧:对于需要套娃的字段值&#xff0c;可以加一个临时列同样使用CONCATENATE函数进行sql拼装 案例&#xff1a; 1.临时列:CONCATENATE(C2, …

HBase(docker版)简单部署和HBase shell操作实践

文章目录 说明HBase部署访问HBase Shell常见命令数据定义语言(DDL) 数据操作语言(DML)通用操作访问HBase WebUI 说明 本文适合HBase初学者快速搭建HBase环境&#xff0c;练习常见shell使用本文参考资料 《大数据技术原理和应用》&#xff08;林子雨 编著 第三版&#xff09;zh…

一文彻底搞懂redis数据结构及应用

文章目录 1. Redis介绍2.五种基本类型2.1 String字符串2.2 List列表2.3 Set集合2.4 Zset有序集合2.5 Hash散列 3. 三种基本类型3.1 Bitmap &#xff08;位存储&#xff09;3.2 HyperLogLogs&#xff08;基数统计&#xff09;3.3 geospatial (地理位置) 4. Stream详解4.1 Stream…

NTRU-Based GSW-Like FHE:Faster Blind Rotation

参考文献&#xff1a; [XZD23] Xiang, B., Zhang, J., Deng, Y., Dai, Y., Feng, D. (2023). Fast Blind Rotation for Bootstrapping FHEs. In: Handschuh, H., Lysyanskaya, A. (eds) Advances in Cryptology – CRYPTO 2023. CRYPTO 2023. Lecture Notes in Computer Scien…

C++_list

目录 一、模拟实现list 1、list的基本结构 2、迭代器封装 2.1 正向迭代器 2.2 反向迭代器 3、指定位置插入 4、指定位置删除 5、结语 前言&#xff1a; list是STL(标准模板库)中的八大容器之一&#xff0c;而STL属于C标准库的一部分&#xff0c;因此在C中可以直接使用…

实现扫码登录

扫码登录是如何实现的&#xff1f; 二维码信息里主要包括唯一的二维码ID,过期的时间&#xff0c;还有扫描状态&#xff1a;未扫描、已扫描、已失效。 扫码登录流程 用户打开网站登录页面的时候&#xff0c;浏览器会向二维码服务器发送一个获取登录二维码的请求。二维码服务器收…

雨云VPS搭建幻兽帕鲁服务器,PalWorld开服联机教程(Windows),0基础保姆级教程

雨云VPS用Windows系统搭建幻兽帕鲁私服&#xff0c;PalWorld开服联机教程&#xff0c;零基础保姆级教程&#xff0c;本教程使用一键脚本来搭建幻兽帕鲁服务端&#xff0c;并讲了如何配置游戏参数&#xff0c;如何更新服务端等。 最近这游戏挺火&#xff0c;很多人想跟朋友联机…

顶象点选验证码

要放假了好颓废。。。。 没啥事儿干&#xff0c;就把之前剩余的顶象点选系列的验证码类型看了下。 之前分享了一篇关于这个顶象的滑块的 DX算法还原_dx算法还原_逆向学习之旅-CSDN博客&#xff0c;感兴趣可以去看看。 咱们以文字点选为例&#xff1a; def get_image_arry(s…

Spring Boot如何统计一个Bean中方法的调用次数

目录 实现思路 前置条件 实现步骤 首先我们先自定义一个注解 接下来定义一个切面 需要统计方法上使用该注解 测试 实现思路 通过AOP即可实现&#xff0c;通过AOP对Bean进行代理&#xff0c;在每次执行方法前或者后进行几次计数统计。这个主要就是考虑好如何避免并发情况…

AI绘画:PhotoMaker Win11本地安装记录!

昨天介绍一个叫PhotoMaker的AI绘画开源项目。挺不错的&#xff01; 通过这个项目可以快速制作特定人脸的AI绘画作品&#xff0c;相比传统的技术效果会好很多&#xff0c;效率也高很多。 今天趁热打铁&#xff0c;本地电脑装装看&#xff0c;并且记录&#xff0c;分享一下&#…