【C++】类和对象详解(2)

news2024/7/6 20:24:34

【C++】类和对象(1)

文章目录

  • 一、类的6个默认成员函数
    • 1.1 构造函数
    • 1.2 析构函数
    • 1.3 拷贝构造函数
    • 1.4 赋值运算符重载
    • 1.5 取地址及const取地址操作符重载
      • const成员


一、类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

在这里插入图片描述


假如我们创建了一个类 - 队列,当我们使用队列这个类创建了一个对象后,有两个必须要考虑的操作:初始化和销毁(特别是在堆区上开辟了空间的)
初始化倒还好,一般人不会忘记,但销毁,很多人会在不经意间忘记,从而导致内存泄漏。c++之父深感其害,于是他想了一个办法,让初始化和销毁操作自动完成,不需要让程序员自己手动调用。而这就有了6个默认函数中的构造函数和析构函数

1.1 构造函数

构造函数的作用是完成对象的初始化。对象在创建之后便会自动调用它

那如何定义一个构造函数呢?
构造函数有以下几个特征:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

在这里插入图片描述


构造函数可以重载,如下:
在这里插入图片描述
但这样的代码有问题,了解函数重载的读者应该知道,全缺省函数和无参函数重载时,虽然在语法上可以,但在函数调用时出问题。
在这里插入图片描述

我们最好保留带缺省参数的哪一个。


如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。那我们为什么不直接使用编译器自己生成的构造函数呢?
因为编译器自己生成的默认构造函数非常奇怪。

c++规定:编译器自己生成的构造函数需要对自定义类型做处理,对内置类型(基本类型如char,int……)不作要求。

这中间有两个关键点:

  1. 编译器对自定义类型做处理:编译器通过调用自定义类型的构造函数来进行初始化。如下:
    在这里插入图片描述
  2. 内置类型不作要求:这就是一个非常奇怪的点,可以说是c++的一个bug,所有的自定义类型都是由内置类型组成的,所有的对自定义类型的初始化是通过调用它的默认构造函数来实现的。 也就是说,本质上都是要对内置类型初始化,但c++又对内置类型的初始化不作要求,这就出现下面的问题:
    在这里插入图片描述
    C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
    在这里插入图片描述

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。


1.2 析构函数

析构函数的作用是对对象进行清理。比如:返回对象在堆上开辟的空间。
对象在生命周期结束时自动调用析构函数。

那如何定义一个析构函数呢?
析构函数的特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

在这里插入图片描述

和构造函数一样,编译器自己生成的析构函数同样有问题:
它不会对内置类型作处理。
所以一般我们也是需要自己写析构函数,除非成员变量全是自定义类型。


1.3 拷贝构造函数

**拷贝构造函数的作用是拷贝一个对象。**它是构造函数的重载。

它的特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
    在这里插入图片描述

拷贝构造函数相比于构造函数有更多的细节问题。
4. 参数为什么要规定为引用?

c++规定了内置类型直接拷贝,自定义类型要调用拷贝构造函数。

在这里插入图片描述

  1. 拷贝有两种类别:浅拷贝和深拷贝。我们定义拷贝构造函数必须要考虑当前拷贝属于那一类别。
    在这里插入图片描述

在这里插入图片描述
如果需要进行深拷贝,使用编译器自己生成的拷贝构造函数则会出错。

请问出错的原因是什么?即使s1和s2的a会指向同一块,但指向同一块空间又不会报错。那它真正错误的原因是什么?
答:不要忘了,s1和s2在销毁时会自动调用析构函数,那将导致s1和s2指向的空间被释放两次,而这就是错误点。


1.4 赋值运算符重载

运算符重载

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

特征如下:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。特别注意.*(这是一个操作符)
    举例
    在这里插入图片描述

但d1.less(d2)看起来够直观吗?不。我们认为最直观应该像下面这样写:
d1 < d2,但<这个运算符不能用来直接比较自定义类型,因此我们需要进行运算符重载

在这里插入图片描述
编译器在运行到d1 < d2时便会调用operator<函数


赋值运算符重载
1.3提到的拷贝构造函数只适用于初始化,而对于一般的赋值操作则需要用到赋值运算符重载
在这里插入图片描述

赋值运算符重载的特征:
参数类型:const type &,传递引用可以提高传参效率
返回值类型:type&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值
返回*this :要复合连续赋值的含义

在这里插入图片描述
其他的运算符重载可以写在全局里,但赋值运算符重载必须定义为成员函数。

在这里插入图片描述
这里需要用到this指针,我在这篇博客【C++】类和对象(1)已经讲解过。之前提到,this指针不能在函数外部显示使用(即不能出现在实参和形参)但可以在成员函数内部显示使用。
之前不明白为什么它规定在函数内部可以显示使用,学了赋值运算符重载,才发现这个规定的妙处,不得不佩服祖师爷的智慧。

但转念一想,我为什么要写返回值呀?我已经完成了赋值操作,为什么还要放回一个引用?
如果只是d1 = d2这种只涉及两个对象的情况,没有返回值也可以。但如果是d1 = d2 = d3 = d4这样,没有返回值将导致错误?
在这里插入图片描述
这就是为什么要写返回值的原因,但为什么要返回引用Date&,而不返回Date呢?
为了提高效率。原因:【C++】5. 引用

同拷贝构造函数一样,编译器自动生成的赋值运算符重载函数也只能进行浅拷贝


运算符重载扩展

  1. 关系运算符重载简化
    假如我要重载 < > == <= >=这五个运算符,实际上我只用实现其中两个 ,<==,剩下可以通过逻辑取反来进行。
    在这里插入图片描述
  2. 前置一元和后置一元
    在这里插入图片描述
    在这里插入图片描述

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递(作用就是占个位置),编译器自动传递
由于后置++存在临时对象tmp, 效率比前置++低,所以自定义类型一般能用前置就用前置

  1. 用+来实现+=时,总的比用+=来实现+多了一次拷贝,效率低了。
    在这里插入图片描述

class Date {
public:
	//构造函数
	Date(int year = 0, int month = 0, int day = 0) {
		_year = year;
		_month = month;
		_day = day;
	}
	//获取天数
	int GetMonthDay(int year, int month) {
		static int dayArr[] = { 0,31,30,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && (year % 4 == 0 && year != 100 == 0) && (year % 400 == 0)) {
			return 29;
		}
		else {
			return dayArr[month];
		}
	}
	//拷贝构造函数
	Date(const Date& d) {
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	//运算符重载
	Date& operator=(const Date& d) {
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	bool operator==(const Date& d) {
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	bool operator<(const Date& d) {
		return (_year < d._year ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day));
	}
	bool operator<=(const Date& d) {
		return (*this < d || *this == d);
	}
	bool operator>(const Date& d) {
		return !(*this < d || *this == d);
	}
	bool operator>=(const Date& d) {
		return !(*this < d);
	}
	bool operator!=(const Date& d) {
		return !(*this == d);
	}
	//前置++
	Date& operator++() {
		_day++;
		return *this;
	}
	//后置++
	Date operator++(int) {
		Date tmp(*this);
		_day++;
		return tmp;
	}
	//前置--
	Date& operator--() {
		_day--;
		return *this;
	}
	//后置--
	Date operator--(int) {
		Date tmp(*this);
		_day--;
		return tmp;
	}
	// 日期-日期 返回天数
	int operator-(const Date& d) {
		int ans = 0;
		int sum1 = _day, sum2 = d._day, sum3 = 0;
		for (int i = 1; i < _month; i++) sum1 += GetMonthDay(_year, _month);
		for (int i = 1; i < d._month; i++) sum2 += GetMonthDay(d._year, d._month);
		sum3 = (_year - d._year) * 365 + (_year - d._year-1) / 4;
		ans = sum1 - sum2 + sum3;
		return *this > d ? ans : -ans;
	}
	// 日期+天数 返回日期
	Date operator+(int day) {
		Date ans(*this);
		ans += day;
		return ans;
	}
	Date& operator+=(int day) {
		_day += day;
		//2023 5 123
		while (_day > GetMonthDay(_year, _month)) {
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month > 12) {
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	void Print() {
		cout << _year << ' ';
		cout << _month << ' ';
		cout << _day << endl;
	}
private:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};

1.5 取地址及const取地址操作符重载

const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

我们之前提到,成员函数会传隐含参数this,即:void Print() <==> void Print(Date* const this),但有时候我不想this指向的内容不能被改变,如果我想将this指向的内容设置为只读,即设置为const Date* const this,该怎么做呢?如下
在这里插入图片描述
那么它有什么用呢?
在这里插入图片描述

因此当函数里面只对this进行读取操作,函数最好加上const.
在这里插入图片描述


之前已经提到了4个默认成员函数,还剩下2个:取地址和const取地址操作符重载在这里插入图片描述
同之前的4个默认成员函数一样,编译器会自己生成。但有差别的是基本上不用我们自己去写。
使用编译器自己生成的就可以了。

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

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

相关文章

吴恩达OpenAI基于ChatGPT的Prompt开发教程

吴恩达OpenAI基于ChatGPT的Prompt开发教程 1.如何给出清晰具体的提示 1.1大模型分类&#xff1a; Base LLM基础大语言模型来源于预训练模型&#xff1a;用已有单词推测下一个单词。 Instruction Tuned LLM指令大语言模型&#xff1a;用人类反馈微调大语言模型优化回答。 当我们…

AMS启动报错 hbase-ams-master.pid doesn‘t exist after starting of the component

感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01; 目录 ​ 一、问题描述 二、解决办法 一、问题描述 Ambari Metrics, Metrics Collector 启动报错如下&#xff1a; stderr: Traceback (most recent call last):File "/var/lib/ambari-agen…

网站被黑后的紧急处理恢复正常步骤是什么?

随着时代的发展网站使用的频率是逐步增加。然而&#xff0c;随着互联网技术的不断进步&#xff0c;网站安全问题也引起了广泛关注。其中&#xff0c;最严重的问题是网站被黑客攻击。那么&#xff0c;何谓网站被黑&#xff1f;它可能会给企业和用户带来哪些风险&#xff1f; 一、…

屏幕录像工具哪个好?这3款录屏工具就很不错!

案例&#xff1a;有没有好用的录屏工具分享&#xff1f; 【每次对电脑屏幕进行录制都要花费我很多时间&#xff0c;录制的内容质量还差&#xff0c;真是事倍功半。听说使用好的录屏工具可以提高录屏的效率&#xff0c;那录屏软件哪款好用&#xff1f;在线蹲一款&#xff01;】…

【AIGC】阿里云服务器配置stable-diffusion-webui

阿里云服务器部署SD全流程, 正在更新&#xff01;&#xff01;&#xff01; 购买阿里云实例开始部署开始运行安装插件中文插件从civitai上下载模型, 加载并利用其绘图 购买阿里云实例 我感觉应该不止我一个&#xff0c;点进阿里云的官网后&#xff0c;发现里面的内容太多&…

【爬虫逆向】Python加密算法大揭秘:应用场景与实现技巧

前言&#xff1a;​ 在我们进行js逆向的时候. 总会遇见一些我们人类无法直接能理解的东西出现. 此时你看到的大多数是被加密过的密文.今天在这里教大家各种加密的逻辑。 Python助学大礼包点击跳转获取 目录 一、一切从MD5开始二、URLEncode和Base64三、对称加密四、非对称加密…

Neo4j之APOC安装与使用样例

1 APOC简介 APOC即Awesome Procedures on Cypher &#xff0c;是 Neo4j 最大和最广泛使用的扩展库&#xff0c;是Neo4j过程和函数的标准实用程序库。它包括 450 多个标准程序&#xff0c;提供实用程序、转换、图形更新等功能。它们得到很好的支持&#xff0c;并且很容易作为单…

超详细canal入门

我们都知道一个系统最重要的是数据,数据是保存在数据库里。但是很多时候不单止要保存在数据库中,还要同步保存到Elastic Search、HBase、Redis等等 一、什么是canal 我们先看官网的介绍 canal,译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供 增量…

MATLAB绘制动画(三)电影动画

就像是电影拍摄一样&#xff0c;MATLAB存储一系列图形&#xff0c;并将这些图像组成一个特殊的矩阵&#xff0c;由函数movie()播放 Z peaks; figure(Renderer,zbuffer); surfc(Z); axis tight manual; set(gca,NextPlot,replaceChildren); for j 1:20surfc(sin(2*pi*j/20)*…

【微信小程序】阶段开发总结

【微信小程序】阶段开发总结 《工欲善其事&#xff0c;必先利其器》 一、页面代码量过多的解决方案 最近开发的小程序页面里面包含非常多的代码&#xff0c;而且一个页面里面分为了很多步骤&#xff0c;根据不同的进度去显示不同的填写内容&#xff0c;因此&#xff0c;首先我…

Jenkins安装详细教程

Jenkins简介 Jenkins是一个开源的软件项目&#xff0c;是基于java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 1.持续的软件版本发布/测试项目 2.监控外部调用执行的工作 最近…

Redis 下载安装

一、redis介绍 redis官网地址&#xff1a;http://www.redis.cn/ Redis是当前比较热门的NoSQL系统之一它是一个开源的、使用ANSI C语言编写的key-value存储系统&#xff08;区别于MySQL的二维表格形式存储&#xff09;和Memcache类似&#xff0c;但很大程度补偿了Memcache的不…

k8s系列-前端镜像打包集成Jenkin spush到仓库

1.General-->源码管理-->构建触发器-->构建环境-->构建-->构建后操作 General-->源码管理-->构建触发器-->构建环境-->构建-->构建后操作 General-->源码管理-->构建触发器-->构建环境-->构建-->构建后操作 General-->源码管…

阿里云ICP备案服务码在哪里申请查看?教程来了

阿里云备案服务码是什么&#xff1f;ICP备案服务码怎么获取&#xff1f;阿里云备案服务码分为免费和付费两种&#xff0c;申请备案服务码是有限制条件的&#xff0c;需要你的阿里云账号下有可用于申请备案服务码的云产品&#xff0c;如云服务器、建站产品、虚拟主机等&#xff…

详解C语言可变参数列表(stdarg头文件及其定义的宏)

前言&#xff1a;在一些函数定义时&#xff0c;&#xff08;&#xff09;内的参数并不是所有的函数参数都是唯一的&#xff0c;如printf函数的定义 这里的省略号就代表printf函数的参数是可以变化的&#xff0c;而对于一些函数其参数是固定的&#xff0c;如strlen 之所以有这样…

Go-Benchmark入门-进阶篇(下)

接上文&#xff1a;Go-Benchmark入门-基础篇&#xff08;上&#xff09; 引言 本篇是进阶篇&#xff0c;围绕最佳实践&#xff0c;介绍项目中可能会用得上的一些技巧和科普更多的 benchmark 知识&#xff0c;也是对本人半个多月实践的一次总结和备忘。 go版本&#xff1a; …

CBA 总决赛激战正酣,线上观赛体验如何保证?

近日&#xff0c;许多球迷都在关注着 CBA 总决赛的激烈赛况。浙江队在征战 CBA 28 年后首次闯入总决赛&#xff0c;并将决赛主场放在了即将承办杭州亚运会正式比赛的奥体中心场馆&#xff0c;使得这轮系列赛成为了浙江篮球史上浓墨重彩的一笔。也难怪球迷现场观赛热情高涨&…

孙鑫VC++第四章 2.简单绘图-绘制

目录 1. 按键消息 2. 绘制线条 2.1 利用SDK全局函数实现画线功能 2.2 利用MFC的CDC类实现画线功能 2.3 利用MFC的CClientDC类实现画线功能 2.4 利用MFC的CWindowDC类实现画线功能 3. 在桌面窗口中画线 3.1 绘制彩色线条 4. 使用画刷绘图 4.1 简单画刷 4.2 位图画刷 …

vue+elementui+nodejs医院疫情防控管理系统3935y

语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 互联网的飞速发展&#xff0c;使人们的生活离不开网络&#xff0c;医院的发展也同样是。现代医院中&#xff0c;不论大小都有各自的管…

想要快速开发东南亚市场?附见效快的方法

外贸人开发东南亚市场的超全指南&#xff0c;觉得有用的话&#xff0c;转发给身边的朋友哦&#xff5e; 一、8大开发东南亚客户的渠道 01 外贸客户开发系统 这个是目前主流的开发客户的渠道&#xff0c;也是效果最好的。可以根据产品关键字、HS编码、SCI编码通过在线数据库…