类和对象(中)- C++

news2024/11/13 14:40:51

个人主页点这里~

1. 类的默认成员函数

在C++中,当你定义一个类时,即使没有显式地声明它们,编译器也会为该类生成一些默认成员函数。这些默认成员函数在特定情况下非常有用,尤其是当你进行对象拷贝、赋值、销毁或动态内存管理时。

     一 . 构造函数

构造函数是特殊的成员函数之一,虽然他的名字是构造,但其实并不是不是用来开辟空间,创建对象的这些局部变量在栈帧创建时就开辟好了,而是在对象实力化时初始化对象的。本质上,就是为初始化功能提供方便(不需要显示调用函数)。

构造函数的特点:
  1. 函数名与类名相同
  2. 无返回值
  3. 构造函数可以重载
  4. 对象实例化会自动调用其对应的构造函数,如果类中没有显式定义构造函数,则C++编译器会自动生成⼀个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  5. 我们不写,编译器默认生成的构造,对内置类型成员变量的初始化没有要求,也就是说是否初始化是不确定的,看编译器。对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错。
  6. 无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数(不传实参就可以调用的构造就叫默认构造)。但是这三个函数有且只有一个存在,不能同时存在。

说明:内置类型就是语原有的int char double  ,自定义类型就是使用class/struct来定义的类型

#include<iostream>
using namespace std;

class Date
{
public:
	// 1.无参构造函数  (默认)
	/*Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}*/
	// 2.带参构造函数
	//Date(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	// 3.全缺省构造函数  (默认)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{
	//如果是1和3,或2和3同时存在,虽然构成重载,但会产生调用歧义
	Date d1;
	d1.Print();
	return 0;
}

一般情况下,所定义的类都需要我们自己写默认构造函数

当定义的类成员变量是另一个自定义类并且有自己的构造函数就不需要写等等.


二 . 析构函数

析构函数也是特殊的成员函数之一,与构造函数功能相反,但他不是对对象本身的清理销毁,完成对象中资源的清理释放工作.(只有当有资源申请的时候,才需要写析构函数)

析构函数的特点:
  • 析构函数名是在类名前加上字符 ~
  • 无返回值
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数
  • 对象生命周期结束时,系统会自动调用析构函数。
  • 跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,自定类成员会调用他的析构函数。
  • 多个对象, 后定义的先析构(后定义的b,在第一次被析构)

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;
	}

	~Stack()//析构函数
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

int main()
{
	Stack a;
	Stack b;

	return 0;
}

一般情况,类成员有需要资源申请时,就需要自己写析构函数.


三 . 拷贝构造函数

如果一个构造函数的第一个参数是该类类型的引用,且有其他参数都有默认值,那么这个函数就叫拷贝构造函数,所以拷贝构造函数是特殊的构造函数。

拷贝构造函数的特点:
  • 拷贝构造是构造函数的重载
  • 拷贝构造函数的第一个参数必须是该类类型对象的引用,如果是传值传参会报错,引发无限递归 
  • C++规定自定义类型对象进行拷贝必须调用拷贝构造,所以这里自定义类型传值传参传值返 都会调用构造完成。
  • C++规定 传值返回会 产生一个临时对象调用拷贝构造 ,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。

  • 未显式定义拷贝构造,编译器会生成自动生成构造函数。自动生成的拷贝构造对内置类型成 员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝)。  对自定义类型成员变量会调用他的拷贝构造。
  • 像类成员都是内置类型且没有资源申请的类,我们可以不写拷贝构造函数,编译器自动生成的就可以完成功能。 但是像上面栈需要申请空间资源的类,自动生成的无法满足功能,就要根据实际情况手动完成拷贝构造函数进行深拷贝
              1.浅拷贝:浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝.两个                   对象都指向同一个内存地址。因此,如果一个对象中的某个引用类型属性被修改了,通                   过浅拷贝得到的新对象也会受到影响.它们共享同一个引用类型属性的内存地址。
              2.深拷贝:深拷贝会创建一个新对象,并且递归地复制原始对象中的所有属性,包括所有                     引用的对象。这样,原始对象和新对象之间就没有任何共享的内存地址了。修改新对象                   的任何属性(包括嵌套对象)都不会影响到原始对象。
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		//有资源申请
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc fail");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	~Stack()//析构函数
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}


	Stack(const Stack& s) //拷贝构造
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);
		if (nullptr == _a)
		{
			perror("malloc fail");
			return;
		}
		memcpy(_a, s._a, sizeof(s._top));
		_capacity = s._capacity;
		_top = s._top;
	}

private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};
int main()
{
	Stack a;
	Stack b(a);

	return 0;
}

深拷贝后两地址不同.


四 . 赋值运算符重载

当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规
定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编
译报错。
关键字:  operator
  • 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有⼀个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
  • 如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个.
  • 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
  • 重载<<和>>时,需要重载为全局函数 因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调用时就变成对象<<cout 不符合使用习惯和可读性。
  • 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持一致.
    通过对日期类的实现完成复习
    #include<iostream>
    using namespace std;
    
    class Date
    {
    	friend ostream& operator<<(ostream& out, const Date& d);
    	friend istream& operator>>(istream& in, Date& d);
    public:
    	// 获取某年某月的天数
    	int GetMonthDay(int year, int month)
    	{
    		static int MonthDayArray[13] = { -1,31,28,31,30,31
    									   ,30,31,31,30,31,30,31 };
    		if (month == 2 && ((year % 400 != 0) || (year % 400 == 0)))
    		{
    			return 29;
    		}
    		return MonthDayArray[month];
    	}
    
    
    	// 全缺省的构造函数
    	Date(int year = 1900, int month = 1, int day = 1);
    
    	//打印
    	void print();
    
    
    	// 拷贝构造函数
      // d2(d1)
    	Date(const Date& d);
    
    
    
    	bool CheckDate();
    
    
    
    	// 赋值运算符重载
      // d2 = d3 -> d2.operator=(&d2, d3)
    	Date& operator=(const Date& d);
    
    
    
    	// 日期+=天数
    	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);
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    
    ostream& operator<<(ostream& out, const Date& d);
    istream& operator>>(istream& in, Date& d);
    
    // 全缺省的构造函数
    Date::Date(int year, int month, int day)
    {
    	_year = year;
    	_month = month;
    	_day = day;
    }
    
    //打印
    void Date::print()
    {
    	cout << _year << '/' << _month << '/' << _day << endl;
    }
    
    // 拷贝构造函数
    // d2(d1)
    Date::Date(const Date& d)
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    }
    
    // 赋值运算符重载
    // d2 = d3 -> d2.operator=(&d2, d3)
    Date& Date::operator=(const Date& d)
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    	return *this;
    }
    
    // 日期+=天数
    Date& Date::operator+=(int day)
    {
    	if (day < 0)
    	{
    		return *this -= (-day);
    	}
    	_day += day;
    	while (_day > GetMonthDay(_year, _month))
    	{
    		_day -= GetMonthDay(_year, _month);
    		_month++;
    		if (_month == 13)
    		{
    			_year++;
    			_month = 1;
    		}
    	}
    	return *this;
    }
    // 日期+天数
    Date Date::operator+(int day)
    {
    	Date tmp = *this;
    	tmp += day;
    	return tmp;
    }
    
    
    
    Date& Date::operator-=(int day)
    {
    	if (day < 0)
    	{
    		return *this += (-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;
    }
    
    
    
    // 前置++
    Date& Date::operator++()
    {
    	*this += 1;
    	return *this;
    }
    // 后置++
    Date Date::operator++(int)
    {
    	Date tmp = *this;
    	*this += 1;
    	return tmp;
    }
    
    
    
    // 后置--
    Date Date::operator--(int)
    {
    	Date tmp = *this;
    	*this -= 1;
    	return tmp;
    }
    // 前置--
    Date& Date::operator--()
    {
    	*this -= 1;
    	return *this;
    }
    
    
    // >运算符重载
    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);
    }
    
    
    // 日期-日期 返回天数
    int Date::operator-(const Date& d)
    {
    	int flag = 1;
    	Date max = *this;
    	Date min = d;
    	if (*this < d)
    	{
    		max = d;
    		min = *this;
    		flag = -1;
    	}
    	int count = 0;
    	while (min < max)
    	{
    		++min;
    		++count;
    	}
    	return flag * count;
    }
    
    ostream& operator<<(ostream& out, const Date& d)
    {
    	out << d._year << "年"
    		<< d._month << "月"
    		<< d._day << "日" << endl;
    	return out;
    }
    
    
    bool Date::CheckDate()
    {
    	if (_year < 1 || _month < 1
    		|| _month>12 || _day<1
    		|| _day>GetMonthDay(_year, _month))
    	{
    		return false;
    	}
    	else
    	{
    		return true;
    	}
    }
    
    
    istream& operator>>(istream& in, Date& d)
    {
    	while (1)
    	{
    		cout << "请依次输入年月日:>";
    		in >> d._year >> d._month >> d._day;
    		if (!d.CheckDate())
    		{
    			cout << "输入日期非法:";
    			d.print();
    			cout << "请重新输入" << endl;
    		}
    		else
    		{
    			break;
    		}
    	}
    	return in;
    }

    分享到这了~

    个人主页点这里~

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

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

相关文章

BatchUpdateException 问题

我们在使用 statement 的 batch api 时、每 20 条数据作为一个事务提交、如果其中一条数据有问题 我们想知道哪一条数据有问题、那么我们可以通过捕获的 BatchUpdatedException 获取到 我们插入 100 条数据、第 21 条和第 41 条是非法数据、会插入失败的 看看日志打印 看看…

函数指针学习

认识函数指针&#xff1a; 函数指针的用处&#xff1a; 回调函数&#xff08;以函数指针为参数的参数&#xff09; 定义带标签的结构体 typedef struct mode { int data; } Node; 标签&#xff08;mode&#xff09;: mode 是结构体的标签名。在这种定义中&#xff0c;mo…

前端:Vue3学习-2

前端:Vue3学习-2 1. vue3 新特性-defineOptions2. vue3 新特性-defineModel3. vue3 Pinia-状态管理工具4. Pinia 持久化插件 -> pinia-plugin-persistedstate 1. vue3 新特性-defineOptions 如果要定义组件的name或其他自定义的属性&#xff0c;还是得回归原始得方法----再…

Codigger 携手 Jupyter:革新数据科学与编程体验

在现今的数字化时代&#xff0c;数据科学正在多个领域内如日中天地发展。数据分析和数据挖掘需求无处不在&#xff0c;对数据的理解与运用&#xff0c;成为许多企业及科研团队取得成功的重要因素。在这个大背景下&#xff0c;Jupyter 项目以其独特的设计理念和强大功能&#xf…

k8s API资源对象ingress

有了Service之后&#xff0c;我们可以访问这个Service的IP&#xff08;clusterIP&#xff09;来请求对应的Pod&#xff0c;但是这只能是在集群内部访问。 要想让外部用户访问此资源&#xff0c;可以使用NodePort&#xff0c;即在node节点上暴漏一个端口出来&#xff0c;但是这…

UNITY UI简易反向遮罩

附带示例资源文件&#xff1a;https://download.csdn.net/download/qq_55895529/89726994?spm1001.2014.3001.5503 大致效果&#xff1a; 实现思路:通过ui shader的模板测试功能实现 通过让想要被突出显示的物体优先渲染并写入模板值,而后再让黑色遮罩渲染并判断模板值进行渲…

机试算法模拟题 服务中心选址

题目描述 一个快递公司希望在一条街道建立新的服务中心。公司统计了该街道中所有区域在地图上的位置&#xff0c;并希望能够以此为依据为新的服务中心选址&#xff1a;使服务中心到所有区域的距离的总和最小。 给你一个数组positions&#xff0c;其中positions[i] [left, ri…

单项链表的原地反转

逻辑图如下所示&#xff1a; 只需要将需要反转的节点放到头节点位置即可&#xff0c; 使用两个指针一个指针指向需要反转的节点&#xff0c;另一个指针指向需要反转的指针的前驱节点&#xff08;其实也就是元链表的头节点&#xff09; 操作过程&#xff1a; 1 2 3 4 5 2 1 …

零基础国产GD32单片机编程入门(十五)CAN通讯详解及实战含源码

文章目录 一.概要二.CAN网络基本组成三.GD32单片机CAN结构与特点1.GD32F103单片机CAN基本结构图2.GD32F103单片机CAN基本特点 四.CAN协议帧格式五.GD32F103C8T6的CAN通讯波特率六.GDF103C8T6的CAN接收过滤器配置七.配置一个CAN通讯数据收发例程八.工程源代码下载九.小结 一.概要…

机器之心 | 挑战Transformer的Mamba是什么来头?作者博士论文理清SSM进化路径

本文来源公众号“机器之心”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;挑战Transformer的Mamba是什么来头&#xff1f;作者博士论文理清SSM进化路径 对 SSM 感兴趣的研究者不妨读一下这篇博士论文。 在大模型领域&#xff0c;…

仕考网:2025年公务员国考备考技巧

公务员考试掌握好备考技巧非常重要&#xff0c;尤其是国考这种竞争比较激烈的考试&#xff0c;仕考网为大家分享一些备考技巧&#xff0c;希望能对大家有所帮助。 1. 在提升行测分数时&#xff0c;可以采用大量的练习题、整理题以及关注往年核心考点的方式。无论处于准备过程的…

JsonPath全英文文档学习

JsonPath全英文文档学习 1、文档地址2、引入依赖3、浏览翻译问题修复4、文档学习4.1、Operator4.2、Functions4.3、Filter 运算符4.4、json示例 5、实战5.1、获取json数组下的多个元素5.2、获取json数组下的多个元素&#xff08;循环遍历时无元素自动占位&#xff09;5.3、获取…

面了阿里大模型算法岗,出门秒挂。。。

最近这一两周看到不少互联网公司都已经开始秋招提前批面试了。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些…

基于springboot+enum配置化实践

前言 Springboot/Springcloud作为微服务开发的经典框架&#xff0c;我想任何一个developer不会排斥。同时&#xff0c;市场越来越多的业务开发均围绕Springboot/Springcloud而展开&#xff0c;也让它成为Java开发的“硬通货”。 基于Spring&#xff0c;诸多低代码的平台的活跃…

基于SpringBoot的校园跑腿系统+LW参考示例

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

spring项目整合log4j2日志框架(含log4j无法打印出日志的情况,含解决办法)

Spring整合Log4j2的整体流程 1&#xff09;导入log4j-core依赖 <!--导入日志框架--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.20.0</version></dependenc…

Linux中的gdb调试器

目录 一、程序的两种模式 二、gdb的常用调试命令 一、程序的两种模式 程序通常有两种模式&#xff0c;分别是debug模式和release模式 debug模式可以调试&#xff0c;但是release模式不支持调试&#xff0c;因为debug模式添加了调试信息&#xff0c;因此debug的程序大小比rel…

[苍穹外卖]-05Redis快速入门

Redis入门 Redis是一个基于内存的key-value结构数据库 基于内存存储, 读写性能高适合存储热点数据(热点商品,咨询,新闻)企业应用广泛中文官网: Redis中文网英文网: https://rsdis.io 下载安装: Redis安装包分为Windows版本和Linux版本, Redis的windows版属于绿色软件, 解压后…

sqlmap简介及安装

sqlmap简介及安装 sqlmap 介绍 sqlmap 是一个开源的渗透测试工具&#xff0c;可以用来进行自动化检测&#xff0c;利用SQL注入漏洞&#xff0c;获取数据库服务器的权限。它具有功能强大的检测引擎&#xff0c;针对各种不同类型数据库的渗透测试的功能选项&#xff0c;包括获取…

【Hot100】LeetCode—198. 打家劫舍

目录 1- 思路动规五部曲 2- 实现⭐198. 打家劫舍——题解思路 3- ACM 实现 原题链接&#xff1a;198. 打家劫舍 1- 思路 动规五部曲 1- 定义 dp 数组 int[] dp new int[nums.length]dp[i] 代表考虑下标i 偷的最大金币数 2- 递推公式 当偷 i &#xff1a; dp[i-2] nums[i] 当…