C++初阶 | [三] 类和对象(中)

news2024/12/24 10:14:06

摘要:类的6个默认成员函数,日期类

如果一个类中什么成员都没有,简称为空类。然而,空类并不是什么成员都没有,任何类在什么都不写时,编译器会自动生成6个默认成员函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

d830f19069b64fedaf2db4eaf7195e58.png

(ps.下文中标题右上角有数字标识的为6个默认成员函数的内容,以示区分) 


1. 构造函数_Constructor¹

构造函数:创建对象?❌ → 初始化

features

  1. 函数名同类名相同
  2. 没有返回值(void
  3. 对象实例化时编译器自动调用80ed5ff83b004172babc0c305b56a511.gif
  4. 可以实现重载
  5. 构造函数不写编译器会默认生成

注意 warning

  • 无参和全缺省会存在歧义,调用的时候不明确,建议实现全缺省构造函数。1f07bc9e33f04cf19bada572c98efdb7.png
  • 调用无参构造函数时,不要加括号,这样写同样会引起歧义,编译器分不清是对象还是函数声明 ( e.g.Date d2();  函数声明——Type function_name(parameter list(可为空))。
  • 构造函数不可以显式调用!

默认构造函数:可以不用传参的构造函数(包括自己没有显式写构造函数而编译器自动生成的,默认构造函数有且只有一个)即无参或全缺省构造函数。

自动生成的构造函数

  1. 自己不写编译器才会自动生成,写了编译器就不会再自动生成其他构造函数。
  2. 自动生成的构造函数对 内置类型 不做处理(对于自定义类型会因具体的编译器而异,语法上规定不做处理,但可能有的编译器会处理),针对这个内置类型不处理的问题,C++11支持在声明处给缺省值,其他地方都没有给初始化值就会用缺省值初始化。(注意:所有的指针都是内置类型,自定义类型的指针也是内置类型!)b6efd991cb4d47c9b168ce90f5b30f7f.png
  3. 对于 自定义类型 会自动调用其自己的默认构造函数。3e9749743fb945fb8dcbfbb74ac73635.png

sum.真正便捷的是自动调用 


2. 析构函数_Destructor²

析构函数:销毁对象?❌(出作用域生命结束编译器自动销毁) → 完成对象中的资源清理工作 ✔

features

  1. 函数名为:~类名
  2. 没有返回值(void),没有参数
  3. 有且只有一个,不可重载(都没有参数肯定无法构成函数重载)
  4. 对象生命周期结束自动调用,调用顺序遵循栈后进先出的原则,即后定义的先析构
  5. 同构造函数一样,自己不写编译器会自动生成

什么情况下需要自己写析构函数?

10367a0783aa4f84aa40190502265599.png


3. 拷贝构造_Copy Constructor³

(注:拷贝构造函数也是构造函数)

值拷贝/浅拷贝

值拷贝:将数据内容完全一样地拷贝一份。但在有些场景中,值拷贝会导致一些问题,如下图所示。

如何解决两次调用析构函数的问题?

  1. 传值传参 → 传引用传参
  2. 实现拷贝构造函数实现深拷贝,如下图(传值传参时,对于自定义类型会调用拷贝构造函数

features

  1. 拷贝构造是构造函数的一个函数重载——函数名为类名,没有返回值。
  2. 拷贝构造参数必须传引用传参(这里的引用推荐加 const 以免将要被拷贝的对象被修改)。否则会导致无穷调用调用拷贝构造函数 → 传值传参 → 值拷贝 → 调用拷贝构造 →…(示例如下图)

 正确的拷贝构造函数示例:

class Date
{
public:
	Date(const int year = 1, const int month = 1, const int day = 1)//全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)//copy constructor
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

自动生成的默认拷贝构造

  1. 对于 内置类型 值拷贝(例如对于上述日期类不用自己写拷贝构造,值拷贝已经能够满足需求)
  2. 对于 自定义类型 自动调用拷贝构造函数

4.操作符重载_operator

class Date
{
public:
	Date(const int year = 1, const int month = 1, const int day = 1)//全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator<(const Date& d)//操作符重载
	{
		if (_year < d._year)
			return true;
		else if (_year == d._day && _month < d._month)
			return true;
		else if (_year == d._day && _month == d._month && _day < d._day)
			return true;
		else
			return false;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 2, 3);
	Date d2(2022, 1, 4);

	cout << (d1 < d2)<<endl;

	return 0;
}

注:上述代码中 d1<d2 → d1.operator<(d2)  (编译器会自动转化为这样显式的调用,所以这与调用其他成员函数是一样的),隐藏的参数 this指针 指向该重载操作符的左操作数 d1,传递的参数 d2 为右操作数。

Rules

  1. 不能创造原本没有的操作符。(e.g.operator@)
  2. 参数中必须有一个自定义类型。
  3. 不能改变操作符原本的含义。
  4. 对于成员函数,第一个参数为 this 指针。即 this 指针所指向的对象一定是操作符的左操作数。
  5. 5个能重载的操作符:::     ?:    .*   sizeof    .

A major design goal of C++ is to let programmers define their own types that are as easy to use as the built-in types.

——《C++ Primer》

操作符重载使得使用自定义类型就像使用内置类型一样便捷。 


5.class Date

1)成员函数声明和定义分离:

类内声明,内外定义。

  1. 缺省值应在函数声明时给出,定义处不能再次定义缺省值,会引发重定义错误。
  2. 定义成员函数要指明类域(在返回值类型之后,函数名之前)——Type classname::function_name(parameter list)

2)constructor:

  • 注意检查日期是否合法(传参可能传递非法日期 )→ 年份没有限制,月份必须在 [1,12] 的区间内,日期受到年份(闰年)和月份的限制。

(下述代码补充说明:GetMonthDay 函数中的 arraystatic 修饰是因为考虑到会频繁调用该函数,导致频繁开辟 array 空间,所以选择将 array 的数据放置在静态区,减少重复开空间,warning:不要对该数组里的数据进行修改!数据存储在静态区,每次调用该函数,修改的行为会被不断累积!!出于这样的考虑,array 可以加 const 保护

int GetMonthDay(const int year, const int month)
{
	static int array[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2)
	{
		if (((year % 100 != 0) && (year % 4 == 0)) || (year % 400 == 0))
			///++array[2];//error!!!!!!!!!!!!!
            return 29;
	}
	return array[month];
}

Date::Date(const int year, const int month, const int day)
{
	if (month <= 12 && day <= GetMonthDay(month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		_year = _month = _day = 0;
		cout << "日期非法" << endl;
	}
}

3)赋值重载_operator=⁴

①赋值与拷贝构造的区别:

赋值重载:将一个变量中的数据 赋 给另一个变量

拷贝构造:用一个已经存在的对象来创建一个新的对象(构造函数主要是完成初始化工作,定义一个自定义类型的变量时会自动调用构造函数!

②连续赋值

赋值的结果返回Date对象(推荐传引用返回)

③ 自己给自己赋值

自己给自己赋值,即 this 指针与传引用传参的参数表示的是同一个存储空间,所以不用继续进行赋值直接返回。综合上述所说,示例代码如下:

const Date& Date::operator=(const Date& d)
{
	if (this == &d)
		return *this;

	_year = d._year;
	_month = d._month;
	_day = d._day;
	return d;
}

④自动生成默认赋值重载函数 

同构造函数和析构函数一样,自己不写会自动生成。自动生成的默认赋值重载函数会完成值拷贝对于日期类,值拷贝已经可以满足需求,因此可以不用自己写赋值重载函数。

4)其他常用操作符重载:

①+= 与 +

  1. 复用的问题:①operator+ 复用 operator+= 两次调用拷贝构造;②operator+= 复用 operator+ 四次调用拷贝构造,一次赋值。所以选择operator+ 复用 operator+= 更好。
  2.  += 负数:注意传参可能为负值的情况

示例代码:

Date& Date::operator+=(int days)
{
	if (days < 0)
		days = -days;

	_day += days;
	int _month_max_day = GetMonthDay(_year, _month);
	while (_day > _month_max_day)
	{
		_day -= _month_max_day;

		//处理月份
		++_month;
		if (_month > 12)
		{
			++_year;
			_month = 1;
		}

		//更新月最大天数
		_month_max_day = GetMonthDay(_year, _month);

	}
	return *this;
}

Date Date::operator+(const int days) const
{
	Date tmp(*this);
	tmp += days;
	return tmp;
}

②前置++ 与 后置++

后置++ 的操作符重载中,语法规定通过参数 int 与前置++构成函数重载,int 只是用来占位,具体传什么内容由编译器处理。例如如下代码的函数声明:(再次提醒只是返回值不同不能构成函数重载)

Date& operator++();//前置++
Date operator++(int);//后置++

5)日期 - 日期 运算

  • 方式一:直接算
  • 方式二:让 小日期++ 直到 等于 大日期,++多少次就相差多少天(其中要用到比较大小的操作符,关于这些操作符的重载在本文第4部分——操作符重载——有一处示例,实现其中一个操作符,其他可以复用,思路简单,在此不多做赘述。以下提供一个实现日期相减的函数示例)
    int Date::operator-(const Date& d) const
    {
    	int count = 0, flag = 1;
    	Date Max_date = *this;
    	Date Min_date = d;
    	if (d > *this)
    	{
    		flag = -1;
    		Max_date = d;
    		Min_date = *this;
    	}
    	while (Min_date < Max_date)
    	{
    		++count;
    		++Min_date;
    	}
    	return count*flag;
    }
    

6. const 对象

在 C++ 入门一文中在介绍常引用(const 引用)时,讨论了关于权限放大的问题,同样,对于 const对象 来说,它们无法调用 非const成员函数——因为会导致权限放大的问题。

 由上图不难看出,一个函数 const 版本非const版本 可以构成函数重载——参数类型不同,this指针的类型分别是 const Type* const Type* const (例如,对于operator[]的操作符重载就很有必要实现 const 和 非const 两个版本)

  • 建议:不做修改的成员函数都加 const 修饰,注意互相复用的函数之间会相互影响!

7. 取地址重载⁵ 及 const 对象取地址⁶

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

class Date
{
public:
	const Date* operator&()const
	{
		return nullptr;
	}

private:
	int _year;
	int _month;
	int _day;
};

 8. 补充:流插入和流提取操作符重载

 以上,我们可以了解到,流提取 >> 的操作数为 cin —— 一个类型为 istream 的对象,右操作数为一个内置类型的变量,同理,流插入 << 的操作数为 cout —— 一个类型为 ostream 的对象,右操作数为一个内置类型的变量。

接下来,我们尝试对日期类的对象实现流插入和流提取的操作符重载(以流提取 >> 为例,流插入同理):因为日期类的成员变量私有在类外无法访问,所以我们尝试在类内实现。同时,考虑到连续流插入/流提取,返回值类型应为ostream/istream。如下代码。

class Date
{
public:  
    ostream& operator<<(ostream& out)
    {
	    out << _year << "/" << _month << "/" << _day << endl;
	    return out;
    }
private:
	int _year;
	int _month;
	int _day;
};

在上述代码中, 可以看到,第一个参数为 隐藏的this指针 ,第二个参数为 ostream对象的引用。则调用该函数即为:d(Type:Date)<<cout(Type:ostream),显然,这样的实现是符合该操作符原本的使用习惯的,在类内实现第一个参数必为 隐藏的this指针,所以考虑在类外实现该函数,为了能够访问内部成员变量,需要用到友元声明(详见类和对象下)。

class Date
{
public:
	friend ostream& operator<<(ostream& out, Date d);

private:
	int _year;
	int _month;
	int _day;
};


ostream& operator<<(ostream& out, Date d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}

cin/cout 的意义:针对自定义类型可以实现重载(scanf/printf 不能很好地支持自定义类型);能够自动识别类型(例如对于 printf 函数如果数据类型改变,相应地也要改变打印格式)


END

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

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

相关文章

【云原生-Kurbernetes篇】K8s的存储卷/数据卷+PV与PVC

这是一个目录标题 一、Kurbernetes中的存储卷1.1 为什么需要存储卷&#xff1f;1.2 存储卷概述1.2.1 简介1.2.2 volume字段 1.3 常用的存储卷类型1.3.1 emptyDir&#xff08;临时存储卷&#xff09;1.3.2 hostPath&#xff08;节点存储卷&#xff09;1.3.3 nfs1.3.4 cephfs 二、…

为React Ant-Design Table增加字段设置 | 京东云技术团队

最近做的几个项目经常遇到这样的需求&#xff0c;要在表格上增加一个自定义表格字段设置的功能。就是用户可以自己控制那些列需要展示。 在几个项目里都实现了一遍&#xff0c;每个项目的需求又都有点儿不一样&#xff0c;迭代了很多版&#xff0c;所以抽时间把这个功能封装了…

用script去做前端html表格分页/排序

前言: 掘弃掉与后端交互做分页和互导,有利有弊吧; 在小数据的时候,如果不停来回朝服务端发送请求,会造成堵塞.于是,放弃了之前的前后端ajax方式去请求分页表格,使用script去弄一个,降低服务器的压力; 整体思路图: 代码构造: {% extends "order_header_same.html" …

​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第9章 软件可靠性基础知识&#xff08;P320~344&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

什么是美国服务器,有哪些优势,适用于什么场景?

​  在互联网发展的过程中&#xff0c;服务器扮演着至关重要的角色。而美国作为全球信息技术的中心&#xff0c;其服务器在全球范围内受到广泛关注。  美国服务器是指在美国本土机房搭建并运行的服务器。其拥有带宽大、优质硬件、售后运维好、位置优越、数据安全性高以及免备…

C/C++输出整数部分 2021年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C输出整数部分 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C输出整数部分 2021年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个双精度浮点数f&#xff0c; 输出其整…

MapApp 地图应用

1. 简述 1.1 重点 1&#xff09;更好地理解 MVVM 架构 2&#xff09;更轻松地使用 SwiftUI 框架、对齐、动画和转换 1.2 资源下载地址: Swiftful-Thinking:https://www.swiftful-thinking.com/downloads 1.3 项目结构图: 1.4 图片、颜色资源文件图: 1.5 启动图片配置图: 2. Mo…

腾讯云服务器多少钱一年?腾讯云服务器88元一年,附优惠购买入口

腾讯云服务器可以以低至88元一年的价格购买&#xff01;这个价格可以说是非常实惠。现在&#xff0c;让我们一起来了解腾讯云服务器的价格以及如何购买优惠的服务器。 如何购买88元一年的腾讯云服务器&#xff1f; 购买腾讯云服务器非常简单&#xff0c;只需按照以下步骤&…

SpringBoot实现IP地址归属地查询

SpringBoot实现IP地址归属地查询 功能特性 标准化的数据格式 每个 IP 数据段的 region 信息都固定了格式&#xff1a; 国家|区域|省份|城市|ISP&#xff0c;只有中国的数据绝大部分精确到了城市&#xff0c;其他国家部分数据只能定位到国家&#xff0c;后前的选项全部是 0。…

聊一聊前端面临的安全威胁与解决对策

前端是用户在使用您的网站或Web应用程序时首先体验到的东西。如果您的Web应用程序的前端受到侵害&#xff0c;它可能会影响整个布局&#xff0c;并造成糟糕的用户体验&#xff0c;可能难以恢复。集成前端安全变得越来越重要&#xff0c;本文将指导您通过可以应用于保护您的Web应…

基于SSM的教学管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

腾讯云服务器价格表查询,腾讯云服务器怎么买便宜?

你是否需要搭建一个属于自己的网站&#xff0c;但是又不知道该如何选择和购买服务器&#xff1f;腾讯云服务器价格表查询&#xff0c;让你轻松了解各款服务器的价格及配置信息&#xff0c;帮助你选择最合适的服务器。同时&#xff0c;我们还为你提供了腾讯云服务器的优惠购买入…

利用jquery对HTML中的名字进行替代

想法&#xff1a;将网页中经常要修改的名字放在一个以jquery编写的js文件中&#xff0c;如果需要修改名字&#xff0c;直接修改js文件中的名字即可。 新建name_07.html文件&#xff0c;写入下面的代码&#xff1a; <!DOCTYPE html> <html> <head><meta …

【docker启动的Jenkins时,遇到时区问题处理】

1、查看容器中的时区 [rootlocalhost jenkins]# docker exec -it jenkins cat /etc/timezone Etc/UTC而本地使用的是Asia/shanghai [rootlocalhost jenkins]# timedatectl | grep Time zoneTime zone: n/a (CST, 0800)###查看 [rootlocalhost jenkins]# cd /usr/share/zoneinf…

UnitTest + Selenium 完成在线加法器自动化测试

1. 任务概述 利用 UnitTest 与 Selenium 编写自动化用例&#xff0c;测试在线加法器中的整数单次加法功能【如123 】 人工操作流程&#xff08;测试 12 是否等于 3&#xff09;&#xff1a; 打开在线加法器点击按钮1&#xff0c;再点击按钮&#xff0c;再点击按钮2&#xff0c…

opencv(2): 视频采集和录制

视频采集 相关API VideoCapture()cap.read()&#xff1a; 返回两个值&#xff0c;第一个参数&#xff0c;如果读到frame&#xff0c;返回 True. 第二个参数为相应的图像帧。cap.release() VideoCapture cv2.VideoCapture(0) 0 表示自动检测&#xff0c;如果在笔记本上运行&…

PHP写一个电商 Api接口需要注意哪些?考虑哪些?

随着互联网的飞速发展&#xff0c;前后端分离的开发模式越来越流行。编写一个稳定、可靠和易于使用的 API 接口是现代互联网应用程序的关键。本文将介绍在使用 thinkphp6 框架开发 电商API 接口时需要注意的要点和考虑的问题&#xff0c;并提供详细的逻辑步骤和代码案例。 1. …

Dubbo协议详解

前言特点应用场景Dubbo协议示例Dubbo协议的不足拓展 前言 Dubbo协议是一种高性能、轻量级的开源RPC框架&#xff0c;主要设计目的是解决分布式系统中服务调用的一些常见问题&#xff0c;例如服务负载均衡、服务注册中心、服务的远程调用等。它支持多种语言&#xff0c;例如Jav…

人工智能基础_机器学习038_中国人寿保费预测(EDA数据探索)_导包_数据探索_---人工智能工作笔记0078

注意 EDA是Exploratory Data Analysis(探索性数据分析)的缩写,它是一种统计分析方法,旨在了解数据的基本特征,并发现数据中的规律和模式。EDA通常是数据分析流程的开始阶段,主要使用可视化工具和统计指标来描述数据的基本特征,如数据的分布、中位数、均值、方差等。通过…

pip 问题

升级pip命令&#xff1a; python -m pip install --upgrade pippip不能下载pytorch&#xff1a; 这个问题我一直没解决。不知道有哪位大佬可以留言给我。把whl文件下载到本地也没有&#xff0c;pip不会进行本地文件夹搜索。