C++——类和对象(中)完结

赋值运算符重载

运算符重载

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型: 返回值类型  operator 操作符 ( 参数列表 )
注意:
不能通过连接其他符号来创建新的操作符:比如 operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型 + ,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少 1 ,因为成员函数的第一个参数为隐
藏的 this
.* :: sizeof ?: . 注意以上 5 个运算符不能重载。这个经常在笔试选择题中出现。
为了更好理解赋值运算符重载 下面用日期类实现运算符重载

日期类实现运算符重载

operator<运算符的实现

Date.h  Date.cpp test.cpp 三个文件实现

Date.h#include<iostream>using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1);void Print();bool operator<(const Date& d);private:int _year;int _month;int _day;
};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;
}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;
}test.cpp
#include"Date.h"void Test1()
{Date d1(2023, 11, 2);Date d2(2023, 11, 3);cout << (d1 < d2) << endl;}int main()
{Test1();return 0;
}

因为d1是自定义出来的对象,运算符不能识别自定义类型对象,只能识别内置类型,因此出现了运算符重载 operator+运算符。根据自己需要来定义返回值的不同 比如上面比较大小 返回真假即可 

operator==运算符实现

Date.h
#include<iostream>using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1);void Print();bool operator<(const Date& d);bool operator==(const Date& d);private:int _year;int _month;int _day;
};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;
}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 _year == d._year&& _month == d._month&& _day == d._day;
}
test.cpp
#include"Date.h"void Test1()
{Date d1(2023, 11, 2);Date d2(2023, 11, 3);cout << (d1 < d2) << endl;cout << (d1 == d2) << endl;
}int main()
{Test1();return 0;
}

 当我们实现完前面两个运算符重载以后,剩余的<= >= != >统统可以复用来实现

operator> >= <= !=运算符实现 

Date.h
#include<iostream>using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1);void Print();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);private:int _year;int _month;int _day;
};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;
}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 _year == d._year&& _month == d._month&& _day == d._day;
}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 || *this == d;
}
bool Date::operator>=(const Date& d)
{return !(*this < d) || *this == d;
}test.cpp
#include"Date.h"void Test1()
{Date d1(2023, 11, 2);Date d2(2023, 11, 3);cout << (d1 < d2) << endl;cout << (d1 == d2) << endl;cout << (d1 > d2) << endl;cout << (d1 != d2) << endl;cout << (d1 <= d2) << endl;cout << (d1 >= d2) << endl;
}int main()
{Test1();return 0;
}

因为类成员作为函数成员重载时,第一个默认的形参为隐含的Date*const this指针,因此实现完两个运算符重载函数后,直接复用即可,简洁且高效。

日期类除了比较大小有意思之外 那么日期的相减 相加同样也有意义

那么在计算日期相减 相加 就需要知道某个月 包含的天数 某年是否为闰年

这个时候就需要我们的Getmonthday函数的定义

Getmonthday函数实现

Date.cpp
int Date::Getmonthday(int year, int month)
{assert(year >= 1 && month >= 1 && month <= 12);int arry[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 arry[month];
}

可以完美帮我们判断闰年 和所对应年 月的天数 方便正确计算 日期的相减相加

也不排除有人会在定义对象的时候给了2023 13 29 月和天显然是不合理的 因此我们需要在成员变量在初始化定义(构造函数)的时候 判断下赋的值正不正确

初始化对象后的判断日期类是否正确

Date.cpp
Date::Date(int year, int month , int day)
{_year = year;_month = month;_day = day;if (_year < 1 || _month < 1 || _month>12|| _day < 1 || _day>Getmonthday(_year, _month)){cout << "非法日期" << endl;Print();}
}test.cppvoid Test1()
{Date d1(2023, 11, 2);Date d2(2023, 13, 3);}int main()
{Test1();return 0;
}

operator+=运算符实现

d1+=50

Date.cpp
Date& Date::operator+=(int day)
{_day += day;while (_day > Getmonthday(_year, _month)){_day -= Getmonthday(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}return *this;
}test.cpp
void Test2()
{Date d1(2023, 11, 2);d1 += 50;d1.Print();}

我们怎么知道最后结果是正确的呢?去网上搜日期计算器就可以了。

 

先进行天数的相加,再判断天数是否大于Getmonthday函数里面月天数的大小,如果大于就先进行当前月天数的相减 然后再++月,再判断月的条件,因为返回类型为Date,出了作用域还在,因此用引用返回。

d1+50也有意义

operator+运算符实现

Date.cpp
Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;}test.cpp
void Test2()
{Date d1(2023, 11, 2);/*d1 += 50;*/d1.Print();Date ret = d1 + 50;ret.Print();}

直接复用+=即可,因为是d1+50 d1本身不改变 先创建一个临时变量 用拷贝构造传this最初的值,再复用+=加上天数 返回tmp临时变量 因为tmp出了作用域不存在 固不用引用返回。

反观上面+=和+的运算符重载实现,-=和-的实现方法也类似

operator-=运算符实现

Date.cpp
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += Getmonthday(_year, _month);}return *this;
}test.cpp
void Test2()
{Date d1(2023, 11, 2);/*d1 += 50;*/d1 -= 50;d1.Print();/*d1.Print();Date ret = d1 + 50;ret.Print();*/}

  

operator-运算符实现

Date.cpp
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}test.cpp
void Test2()
{Date d1(2023, 11, 2);/*d1 += 50;*//*d1 -= 50;d1.Print();*//*d1.Print();Date ret = d1 + 50;ret.Print();*/Date ret = d1 - 50;d1.Print();ret.Print();}

那么C语言有前置++和后置++ 那么运算符重载自定义对象时怎么分辨呢?

C++之父为了区分前置++和后置++ 在后面++后面加了个int形参

d1++ ++d1 也有意义

operator++(前置)运算符实现

Date.cpp
Date& Date:: operator++()
{*this += 1;return*this;
}test.cpp
void Test2()
{Date d1(2023, 11, 2);/*d1 += 50;*//*d1 -= 50;d1.Print();*//*d1.Print();Date ret = d1 + 50;ret.Print();*//*Date ret = d1 - 50;d1.Print();ret.Print();*/++d1;d1.Print();++d1;d1.Print();}

因为前置++自己本身也会跟着随之改变 所以引用返回this本身。 

operator++(后置)运算符实现

Date.cpp
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}test.cpp
void Test3()
{Date d1(2023, 11, 2);d1.Print();d1++;d1.Print();
}

operator--(前置)运算符实现

Date.cpp
Date& Date::operator--()
{*this -= 1;return *this;
}test.cpp
void Test3()
{Date d1(2023, 11, 2);/*d1.Print();d1++;d1.Print();*/--d1;d1.Print();--d1;d1.Print();
}

operator--(后置)运算符实现 

Date.cpp
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}test.cpp
void Test3()
{Date d1(2023, 11, 2);/*d1.Print();d1++;d1.Print();*//*--d1;d1.Print();--d1;d1.Print();*/d1.Print();d1--;d1.Print();}

 

既然日期-天数 日期-月 日期++都有意义 那么日期-日期也有意义

比如计算2023到2024年还有多少天

operator-(日期-日期)

Date.cpp
int Date::operator-(const Date& d)
{// 假设左大右小int flag = 1;Date max = *this; //this指向d1Date min = d; //d2// 假设错了,左小右大if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}test.cpp
void Test4()
{Date d1(2023, 11, 2);Date d2(2024, 1, 1);cout << (d1 - d2) << endl;
}

但我们在+= 和-=忽略掉了一个问题 就是当给天数 给成负值怎么办?比如d1+=(-50)  d1+(-50)

 当我们用我们上面实现的+=时

d1+=50   d1=d1+(-50)  d1=2-50=-48

void Test4()
{Date d1(2023, 11, 2);//Date d2(2024, 1, 1);d1 += -50;d1.Print();//cout << (d1 - d2) << endl;
}

天数变成了负值 但那个日期计算器人家是能正常计算的

为了能够正确计算负天数,我们只需要加个一个判断条件即可 

 当天数小于0时  d1+=-50 变成了 d1=d1-(-50)      即d1+=50 d1=d1+50;

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)
{if (day < 0){return *this += (-day);}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += Getmonthday(_year, _month);}

日期类实现全部代码

Date.h

#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1);void Print();int Getmonthday(int year, int month);bool operator<(const Date& d)const;bool operator==(const Date& d)const;bool operator>(const Date& d)const;bool operator!=(const Date& d)const;bool operator<=(const Date& d)const;bool operator>=(const Date& d)const;Date& operator+=(int day);Date operator+(int day)const;Date& operator-=(int day);Date operator-(int day)const;Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);int operator-(const Date& d)const;private:int _year;int _month;int _day;
};

Date.cpp


#include"Date.h"Date::Date(int year, int month , int day)
{_year = year;_month = month;_day = day;if (_year < 1 || _month < 1 || _month>12|| _day < 1 || _day>Getmonthday(_year, _month)){cout << "非法日期" << endl;Print();}
}void Date::Print()const
{cout << _year << "_" << _month << "_" << _day << endl;
}bool Date::operator<(const Date& d)const
{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)const
{return _year == d._year&& _month == d._month&& _day == d._day;
}bool Date::operator>(const Date& d)const
{return !(*this < d);
}
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}
bool Date::operator<=(const Date& d)const
{return *this < d || *this == d;
}
bool Date::operator>=(const Date& d)const
{return !(*this < d) || *this == d;
}
int Date::Getmonthday(int year, int month)
{assert(year >= 1 && month >= 1 && month <= 12);int arry[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 arry[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)const
{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)const
{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--()
{*this -= 1;return *this;
}
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}int Date::operator-(const Date& d)const
{// 假设左大右小int flag = 1;Date max = *this; //this指向d1Date min = d; //d2// 假设错了,左小右大if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}

test.cpp

#include"Date.h"//void Test1()
//{
//	/*Date d1(2023, 11, 2);
//	Date d2(2023, 13, 3);*/
//	
//	/*cout << (d1 < d2) << endl;
//	cout << (d1 == d2) << endl;
//	cout << (d1 > d2) << endl;
//	cout << (d1 != d2) << endl;
//	cout << (d1 <= d2) << endl;
//	cout << (d1 >= d2) << endl;*/
//}//void Test2()
//{
//	Date d1(2023, 11, 2);
//	/*d1 += 50;*/
//	/*d1 -= 50;
//	d1.Print();*/
//	/*d1.Print();
//	Date ret = d1 + 50;
//	ret.Print();*/
//	/*Date ret = d1 - 50;
//	d1.Print();
//	ret.Print();*/
//	/*++d1;
//	d1.Print();
//	++d1;
//	d1.Print();*/
//
//}//void Test3()
//{
//	Date d1(2023, 11, 2);
//	/*d1.Print();
//	d1++;
//	d1.Print();*/
//	
//	/*--d1;
//	d1.Print();
//	--d1;
//	d1.Print();*/
//	d1.Print();
//	d1--;
//	d1.Print();
//	
//}
void Test4()
{Date d1(2023, 11, 2);//Date d2(2024, 1, 1);d1 += -50;d1.Print();//cout << (d1 - d2) << endl;
}
int main()
{//Test1();//Test2();//Test3();Test4();return 0;
}

const成员

加上const后为什么不能打印出来呢?因为这里是权限的放大

从const Date*const this变成了 Date*const this 形成了权限的放大

那怎么解决呢?祖师爷决定在括号加上一个const(声明和定义后面都要加上)

当加个const以后 const就修饰了Date*const this变成了const Date*const this  const本质是修饰this

从权限的放大变成了权限的平移 就能打印了 

那么非const对象能调用const函数吗 ?

答案是可以的,因为Date*const this 变成了 const Date*const this 形成了权限的缩小

权限可以平移和缩小 但不能放大。

那么所有函数都能定义成const函数吗?是不可以的,当需要修改成员变量的成员函数,不能改成const。

但能定义成const的成员函数都应该定义成const

这样const对象和非const对象都可以调用(const权限的平移和const权限的缩小)

要修改成员变量的成员函数,不能定义成const

因此在日期类成员函数在不修改成员变量的成员函数后面都可以加上const

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

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

 当屏蔽掉非const取地址操作函数,非const会去调用const取地址操作函数

这两个取地址操作符重载一般使用编译器默认生成的就可以。

只有特殊情况,才需要重载,比如想让别人获取到指定的内容! 

 不想让别人获取地址可以返回空指针nullptr或者返回自己编造的一个地址。

operator实现<<流插入

为什么不能打印出d1内容,因为<<不识别自定义类型对象 为什么可以识别内置类型呢?

因为C++已经头文件里面的库函数已经帮忙实现了

 为了实现识别自定义类型对象,自己照猫画虎实现一个即可。

Date.h 声明

Date.cpp 定义

但是它有一个问题,就是访问不了私有成员变量,我们暂且先取消掉private,把成员变量放开 

 

但出现了问题 cout<<d1打印不了  d1<<cout可以打印,这又是为什么呢?

 

因为双目操作数的运算符,规定第一个参数必须是左操作数(*this),第二个参数必须是右操作数

d1<<cout 转换  d1.operator<<(&d1,cout);  cout<<d1 第一个参数是cout 第二个是*this所以不行。

那么为了规定传参,流插入的实现只能在全局进行实现,因为成员函数Date对象默认占据第一个位置。

在全局实现又有两个问题,第一个不能访问私有成员变量,第二个不能支持连续插入

 

我们给流插入返回值就能解决连续cout的问题

 

那么访问不了私有成员变量怎么解决呢?这时候就要用到我们的友元函数 

  

友元函数就是在类里面成员函数 前面加个friend 表面是友元函数 我和你是好朋友,可以去你家里玩私有物品,比如游戏机 上厕所等。

那么流提取的实现与流插入相仿

operator实现>>流提取

 流本质是为了解决,自定义类型的输入和输出问题

printf scanf 无法解决自定义类型的输入输出问题面向对象 + 运算符重载解决.

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

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

相关文章

【广州华锐互动】VR野外求生技能学习,让你感受真实的冒险之旅!

随着科技的迅速发展&#xff0c;虚拟现实(VR)技术为人们提供了一个全新的、身临其境的探险体验。通过将用户带入一个仿真的、沉浸式的虚拟环境&#xff0c;VR互动体验让人们在安全的氛围中感受到野外探险的乐趣。本文将从视觉呈现、沉浸式体验、交互性和应用范围四个方面&#…

前K个高频单词(Java详解)

一、题目描述 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c; 按字典顺序 排序。 示例1&#xff1a; 输入: words ["i", "love&…

YOLO目标检测——夜间车辆检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;智能交通监控系统、自动驾驶系统、夜间行车安全辅助系统等数据集说明&#xff1a;夜间车辆检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0…

我用 LangChain 打造自己的 LLM 应用项目

随着LLM的技术发展&#xff0c;其在业务上的应用越来越关键&#xff0c;通过LangChain大大降低了LLM应用开发的门槛。本文通过介绍LangChain是什么&#xff0c;LangChain的核心组件以及LangChain在实际场景下的使用方式&#xff0c;希望帮助大家能快速上手LLM应用的开发。 技术…

VS Code 开发 Spring Boot 类型的项目

在VS Code中开发Spring Boot的项目&#xff0c; 可以导入如下的扩展&#xff1a; Spring Boot ToolsSpring InitializrSpring Boot Dashboard 比较建议的方式是安装Spring Boot Extension Pack&#xff0c; 这里面就包含了上面的扩展。 安装方式就是在扩展查找 “Spring Boot…

RIP路由配置

RIP路由配置步骤与命令&#xff1a; 1.启用RIP路由&#xff1a;router rip 2.通告直连网络&#xff1a;network 直连网络 3.启用RIPv2版本&#xff1a;version 2 4.禁用自动汇总&#xff1a;no auto-summary 注意&#xff1a;静态路由通告远程网络&#xff0c;动态路由通告…

apollo云实验:定速巡航场景仿真调试

定速巡航场景仿真调试 概述启动仿真环境仿真系统修改默认巡航速度 实验目的福利活动 主页传送门&#xff1a;&#x1f4c0; 传送 概述 自动驾驶汽车在实现落地应用前&#xff0c;需要经历大量的道路测试来验证算法的可行性和系统的稳定性&#xff0c;但道路测试存在成本高昂、…

你渲染的3ds Max效果图为什么这么假?原来问题出在这!

有许多设计新手刚开始做效果图时&#xff0c;常常抱怨自己的作品看起来太假了&#xff0c;但又不知道怎么改。根据我的经验总结&#xff0c;我整理了以下几点可能导致你的3ds Max渲染效果图显得不够真实的原因。 1.三维模型不精致 如果模型细节不够精细&#xff0c;渲染出来的…

FFmpeg——使用Canvas录制视频尚存问题的解决方案

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

该虚拟机似乎正在使用中。如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所有权。否则,请按“取消(C)”按钮以防损坏。

问题描述 该虚拟机似乎正在使用中。 如果该虚拟机未在使用&#xff0c;请按“获取所有权(T)”按钮获取它的所有权。否则&#xff0c;请按“取消©”按钮以防损坏。 配置文件: D:\RedHat\Red Hat Enterprise Linux 8 64 位.vmx。 获取所有权&#xff1a; 看到这里我的心凉…

APLHA开发板系统启动

一. 简介 前面学习了 uboot的移植&#xff0c;在 NXP的 Kernel内核源码里添加 ALPHA开发板的工作。本文为设置CPU主频做准备&#xff0c;开发板从 Nand-Flash中启动根文件系统。 具体来说&#xff0c;我的目的是开发板能正常启动&#xff08;从Nand-Flash加载根文件系统&#…

基于Python+Networkx的最短路径

networkx是一个用Python语言开发的图论与复杂网络建模工具&#xff0c;内置了常用的图与复杂网络分析算法&#xff0c;可以方便的进行复杂网络数据分析、仿真建模等工作。利用networkx可以以标准化和非标准化的数据格式存储网络、生成多种随机网络和经典网络、分析网络结构、建…