C++类与对象(中)第二篇

前言:

C语言中使用运算符是对内置类型的数据进行操作,但是在C++中有了对象,导致对象无法通过运算符进行运算,故引入了运算符重载即需要重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作;运算符重载就是对已有的运算符重新进行定义而且不能改变它本来的寓意,以达到适应不同的数据类型

运算符重载

class Date
{
private:int _year;int _month;int _day;
public://构造函数Date(int year=2023, int month=12, int day=20){_year = year;_month = month;_day = day;}
}
int main()
{Date d1(2023,12,20)Date d2(2000,10,20)return 0;
}

 定义了两个日期类对象d1,d2,如果想判断两个日期是否相同,需要比较各自的年、月、日,但是对象是自定义类型,运算符只支持内置类型的数据进行相关操作,此时我们需要重新定义运算符的规则,通过函数传参的形式可以对对象进行比较,但是函数名的命名可以千奇百怪,为规避命名不规则,增强代码的可读性,一律采用 operator运算符 同时也就实现自定义类型直接使用运算符

返回值类型  operator运算符(形参列表)
{....
}
采取成员函数的声明与定义相分离的方式,即类声明存放于Date.h文件中,成员函数的定义存放于Date.cpp文件中, 运算符重载为成员函数 ;一个类重载那些运算符呢?取决于运算符对于该类有没有实际意义;
// Date.h文件
class Date
{
private:int _year;int _month;int _day;
public://函数声明//d1==d2bool operator==(const Date& d);//显示对象信息void Print();
}
//Date.cpp文件//显示对象信息
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
//d1==d2
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}

!=运算符重载

对于类中的成员函数而言,第一个参数为隐藏的this指针;

重载双目运算符,只需要设置一个参数作为右操作数,而左操作数即为this指针指向的对象;

重载单目运算符,不必设置参数,操作数只有一个即为this指针指向的对象;

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

>运算符重载

bool operator>(const Date& x, const Date& y)编译错误:参数太多,==运算符只有两个操作数;

运算符重载函数作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;

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

>=运算符重载

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

<运算符重载

//d1<d2  注: 小于的对立面为大于等于;
bool Date::operator<(const Date& d)
{return !((*this) >= d);
}

<=运算符重载

//d1<=d2 注: 小于等于的对立面为大于;
bool Date::operator<=(const Date& d)
{return !((*this) > d);
}

=运算符重载

赋值运算符的重载格式:

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

赋值运算符只能重载成类的成员函数不能重载成全局函数

原因:赋值运算符如果不显式实现,编译器会自动生成一个默认的赋值运算符重载以值的方式逐字节拷贝;此时用户再在类外自己实现一个全局的赋值运算符重载与编译器在类中生成的默认赋值运算符重载冲突,故赋值运算符重载只能是类的成员函数

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

+=运算符重载

由于日期类对象的成员变量_month(月份)非固定,先获取每个月份的具体天数,然后实现日期+=函数的功能;

//获取每个月份的天数
int Date::GetMonthDay(int year, int month)
{assert(year >= 1 && (month <= 12 && month >= 1));int MonthArray[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 MonthArray[month];
}
//日期+=天数
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;
}

前置++运算符重载

前置++:返回+1之后的结果;
注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率;
//++d1 前置++无参
Date& Date::operator++()
{*this = *this + 1;return *this;
}

后置++运算符重载

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载,
C++规定 后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递;
注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this+1 ,而temp是临时对象,因此只能以值的方式返回,不能返回引用;
//d1++
Date Date::operator++(int x)
{Date tmp(*this);*this += 1;return tmp;
}

 前置--运算符重载

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

后置--运算符重载

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

-运算符重载

//d1-d2
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 count * flag;
}

<<流插入运算符重载

流插入运算符为 “<<",与cout配合使用,其作用是支持内置类型的数值输出,若要使用cout,需要包含头文件#include<ostream>;

cout为ostream类的对象,cin为istream类的对象;

为了使自定义类型直接使用运算符,实现流插入运算符重载,<<流插入运算符是双目运算符,具有两个操作数,对于日期类,一个操作数为日期类对象,一个操作数为ostream类对象(cout) ,若在类中存放<<流插入运算符重载的声明,则形式如下:

void operator<<(ostream& out)//成员函数第一个参数为this指针,本质形式如下
//void operator<<(Date* const this,ostream& out)

当用户在主函数调用时cout<<d1时,导致编译出错;若调用d1<<cout,编译通过;

原因:成员函数第一个参数为隐藏的this指针,将<<流插入运算符的左操作数传递给this指针,所以调用d1<<cout,编译通过;

上述调用形式 d1<<cout会导致代码可读性太差,为保证逻辑自洽,将<<运算符重载为全局函数,但是涉及到成员变量私有问题,不能直接访问成员变量,采用友元函数解决此问题,将友元函数在类中声明;声明形式如下:

//Date.h
class Date
{
private:int _year;int _month;int _day;
public:friend ostream& operator<<(ostream& out,const Date&d)//友元函数的声明//...
}
//Date.cpp文件
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

>>流提取运算符重载

 流提取运算符重载的形式与流插入运算符重载实现类似,依然重载为全局函数,在类中声明为友元函数;

//Date.h
class Date
{
private:int _year;int _month;int _day;
public://友元函数的声明friend istream& operator>>(istream& in, Date& d);//...
}
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

 运算符重载的注意事项:

  1. 不能通过连接其他符号(C++中不存在的运算符)来创建新的操作符:比如operator@;
  2. 重载操作符必须有一个类类型参数(因为运算符重载主要是为了让自定义类型能够像内置类型那样去使用运算符);
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义(因为运算符重载主要是为了让自定义类型能够像内置类型那样去使用运算符,内置类型不需要运算符重载);
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this指针;
  5. .* :: sizeof ?: .  注意以上5个运算符不能重载

const成员函数

const修饰普通变量

const修饰普通变量的本质为定义了一个常量,形式如下:

Type const ValueName=value;
const Type valueName=value;

此时变量具有了常属性,变量里的数据只能被访问而且不能被修改,也就是意味着“只读”;

const修饰指针

const修饰指针变量本身,指针变量本身不允许被修改,即一旦得到了某个变量的地址,不能再指向其它变量;

int n = 20;
int* const p = &n; //const修饰指针变量本身

const修饰指针变量指向的内容,不可以通过指针修改其指向的变量的值,但是指针变量可以被修改;

int n = 10;
const int *p = &n;//const修饰指针变量指向的内容,即n=10;

const修饰函数形参

const修饰指针形参,形式如下:

void function(const Date* Var);

将外部实参的地址赋值给用const修饰的指针形参,用户无法通过指针修改其所指的外部实参,保护了数据的安全性;

void function(Date* const Var);

表示指针形参不会改变,用户依然可以通过指针修改传过来的外部实参,无法保证外部数据的安全性,const指针形参毫无意义;

 const修饰引用形参,形式如下:

void function(const Date& Var); //引用参数在函数内不可以改变

参数为引用,将外部实参传递给引用形参,传递的是外部实参本身,无需进行拷贝,增加了效率,同时参数是const引用,无法通过引用修改实参,保证了外部数据的安全性;

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机安装windows2012和虚拟机安装国产系统deepin

虚拟机安装windows2012和虚拟机安装国产系统deepin 一.安装windows20121.安装VMWare虚拟机2.1.注意点一&#xff1a;VMWare虚拟网卡2.2.注意点二&#xff1a;配置虚拟网络编辑器3.安装配置Windows Server 2012 R2 二.虚拟机安装deepin1.deepin官网下载ios镜像2.deepin下载合适的…

统计定界子数组的数目

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 给你一个整数数组 nums 和两个整数 minK 以及 maxK 。 nums 的定界子数组是满足下述条件的…

老胡的周刊(第121期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 clone-voice[2] 一个带 web 界面的声音克隆工…

【大数据】NiFi 的基本使用

NiFi 的基本使用 1.NiFi 的安装与使用1.1 NiFi 的安装1.2 各目录及主要文件 2.NiFi 的页面使用2.1 主页面介绍2.2 面板介绍 3.NiFi 的工作方式3.1 基本方式3.2 选择处理器3.3 组件状态3.4 组件的配置3.4.1 SETTINGS&#xff08;通用配置&#xff09;3.4.2 SCHEDULING&#xff0…

设计模式--职责链模式

实验15&#xff1a;职责链模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解职责链模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用职责链模式解决实际问题。 [实验任务]&#xff1a;财务审批 某物资管理系统…

Window系统显存不足时利用共享内存继续运行

存在问题 在进行实验过程中&#xff0c;时常碰到显存不足的问题&#xff0c;此时要么需要减小模型体积&#xff0c;要么减小batch-size大小&#xff0c;但这势必会对结果造成一定影响&#xff0c;此时我们可以使用GPU共享内存的方法来扩充显存。 解决方法 GPU有几个重要参数…

otter-harbor同步

一. 部署及依赖 otter Github (一). 服务启动 1. mysql 5.6版本以上&#xff0c;作为 otter-manger 使用的数据库 # mysql docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql:5.7 --character-set-serverutf8mb4 --collation-serverutf8mb4_un…

2023年12月16日~12月22日(自适应反馈机制下基于卷积神经网络的高清晰反射波反演算法:CNN-RWI)

标题&#xff1a;Adaptive Feedback Convolutional-Neural-Network-Based High-Resolution Reflection-Waveform Inversion 全波形反演&#xff08;FWI&#xff09;是一种非线性拟合观测地震记录从而获得高清晰速度模型的最优化算法。FWI能够通过拟合浅层初至波和反射波获得较准…

<avatar: frontiers of pandora>技术overview

https://www.eurogamer.net/digitalfoundry-2023-avatar-frontiers-of-pandora-and-snowdrop-the-big-developer-tech-interview https://www.youtube.com/watch?vLRI_qgVSwMY&t394s 主要来自euro gamer上digital foundry对于avatar的开发团队Massive工作室的采访&#xf…

Java多线程、线程池及线程同步(synchronized关键字、悲观锁、乐观锁)

1.进程与线程定义 进程包含线程&#xff0c;如一个百度网盘进程&#xff0c;该进程的线程可以有上传&#xff0c;下载。 2.创建线程的三种方式 方式1-继承Thread类 方式2-实现Runnabled接口 1.常规写法 2.匿名内部类写法 方式3-实现Callable接口 示例代码&#xff1a; f1.get…

实现打印一个数字金字塔。例如:输入5,图形如下图所示

1*12**123***1234**** 12345*****#include<stdio.h> void main() {int i,j,l,n,k;scanf("%d",&n);/**********Program**********//********** End **********/ } 当我们拿到这个题目的时候可以看见题目给了我们五个变量&#xff0c;其中n是我们输入的数…

Linux:jumpserver V3的安装与升级(在线离线)(2)

官方文档写的非常详细&#xff0c;我这篇文章时间长了&#xff0c;会随着官方版本更新而落后 JumpServer - 开源堡垒机 - 官网https://www.jumpserver.org/安装和升级在官网也有详细的信息&#xff0c;我写本章是为了记录一下实验 我的系统是centos7.9 在线安装 在确定我们可…