当前位置: 首页 > news >正文

C++类与对象(5)—流运算符重载、const、取地址

目录

一、流输出

1、实现单个输出

2、实现连续输出

二、流输入

  总结:

三、const修饰

四、取地址

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

五、[ ]运算符重载


一、流输出

1、实现单个输出

创建一个日期类。

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

以前我们在日期类中想要输出日期,都需要在类中自己创建一个用于输出的成员函数。

	void Print(){cout<< _year << "年" << _month << "月" << _day << "日" << endl;}

对于内置类型我们可以直接用cout<<输出其值。

我们也可以重载流提取<<实现输出内置类型的成员值,首先来了解一下cout的由来。

  • iostream 是 C++ 标准库中的一个头文件,它包含了用于输入和输出的流类的定义。iostream 头文件中定义了 istream 和 ostream 这两个基类,它们分别用于输入和输出操作。

  • ostream 是 iostream 头文件中定义的一个类,它是输出流的基类。ostream 类提供了输出操作的基本功能和接口,例如 << 操作符用于输出数据到流中。

  • cout 是 ostream 类的一个对象,它是标准输出流对象。cout 对象可以使用 << 操作符将数据输出到标准输出设备(通常是控制台)。

iostream 是一个头文件,ostream 是 iostream 中定义的输出流基类,而 cout 是 ostream 类的一个对象,用于将数据输出到标准输出设备。通过使用 cout 对象和 << 操作符,我们可以方便地将数据输出到控制台。

 现在在类中实现<<重载:

	void operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;}

ostream& 是一个引用类型,表示对输出流对象的引用,通过使用 ostream& 引用类型,可以将输出流对象传递给操作符重载。

 当我们要使用运算符重载<<时,需要使用如下形式:

d1 << cout;//或者d1.operator<<(cout);

运算符重载<<的第一个参数为左操作数,第二个参数为右操作数。

虽然这种形式可以输出我们想要的结果,但这与我们使用的cout<<d1这种常规方式有所出入。

我们可以对其进行修改,将<<运算符重载作为全局函数,将输出流对象的引用作为第一个参数,日期类对象的引用作为第二个参数。

void operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

同时,为了使全局<<运算符重载能够访问到日期类对象d的私有成员变量,可以在日期类中创建友元函数声明,这样就可以访问对象的成员了。

friend void operator<<(ostream& out, const Date& d);

下面来测试一下: 

class Date
{friend void operator<<(ostream& out, const Date& d);
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout<< _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};
void operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}void Test()
{Date a(2023, 11, 24);a.Print();cout << a;
}int main()
{Test();return 0;
}

 成功实现运算符<<的重载。

 

2、实现连续输出

如果是下面这种连续输出呢?

void Test2()
{Date a(2023, 11, 24);Date b(2023, 11, 25);cout << a << b << endl;
}

这时编译器会报错。 

 

 为了支持连续输出的形式,我们需要为<<重载增加返回值。

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

同时,友元函数声明也要修改一下:

friend ostream& operator<<(ostream& out, const Date& d);

 测试一下:

class Date
{friend ostream& operator<<(ostream& out, const Date& d);
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout<< _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
void Test2()
{Date a(2023, 11, 24);Date b(2023, 11, 25);cout << a << b << endl;
}
int main()
{Test2();return 0;
}

成功实现连续输出: 

二、流输入

iostreamistream 和 cin 是 C++ 中用于输入的相关类和对象。

  • iostream 是 C++ 标准库中的一个头文件,它包含了用于输入和输出的流类的定义。iostream 头文件中定义了 istream 和 ostream 这两个基类,分别用于输入和输出操作。

  • istream 是 iostream 头文件中定义的一个类,它是输入流的基类。istream 类提供了输入操作的基本功能和接口,例如 >> 操作符用于从流中读取数据。

  • cin 是 istream 类的一个对象,它是标准输入流对象。cin 对象可以使用 >> 操作符从标准输入设备(通常是键盘)读取数据。

总结:iostream 是一个头文件,istream 是 iostream 中定义的输入流基类,而 cin 是 istream 类的一个对象,用于从标准输入设备读取数据。通过使用 cin 对象和 >> 操作符,我们可以方便地从键盘输入数据。

 下面来实现流提取>>运算符重载,与流插入<<实现方式相同。

istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

 测试一下

class Date
{friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout<< _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
void Test3()
{Date d;cin >> d;cout << d;
}
int main()
{Test3();return 0;
}

成功实现流提取运算符重载。 

总结:

如果类的声明和定义是分文件的,我们一般把流提取和流插入运算符重载放到作为内联函数放到头文件中,这样省去了链接的过程,在编译过程就能call地址。

inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
  • 在C++中,如果一个成员函数直接在类内部定义,它会被视为内联函数。内联函数的定义与声明都在类的定义中,这样编译器可以在调用处将函数的代码插入到调用位置,而不是通过函数调用的方式执行。
  • 通常情况下,短小的函数适合作为内联函数,因为内联函数的调用开销较小,可以避免函数调用的额外开销。将这样的函数定义在类内部可以方便地将其声明和定义放在一起,提高代码的可读性和维护性。
  • 然而,需要注意的是,编译器是否将一个在类内部定义的成员函数视为内联函数,最终还是由编译器决定。编译器可能会根据一些因素(如函数的复杂性、调用频率等)来决定是否将其内联展开。
  • 总之,将短小的函数定义在类内部可以被视为内联函数,这样可以提高代码的执行效率和可读性。但最终是否内联展开还是由编译器决定。

三、const修饰

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

我们来看下面代码:

class A {
public:void Print() {cout << _a << endl;}
private:int _a = 1;
};
int main()
{A aa;aa.Print();return 0;
}

成功输出: 

如果用const修饰aa,这样可以吗? 

const A aa;

编译后程序报错: 

 

这是因为造成了权限放大的问题。

 Print函数的参数是A*this,aa的类型是const A*,所以aa调用Print函数会造成权限放大,而且如果权限平移,在this指针前用const修饰,这样也是禁止的,我们不能修改this指针。

这时有一个新的间接方法:

  • 语法规定叫const成员函数,const修饰*this,也就意味着this的类型变成const A*类型。
  • 内部不改变成员变量的成员函数时,最好加上const,const对象和普通对象都可以调用。

 这时我们就可以对C++类与对象(4)—日期类的实现这篇文章的Date.h文件中部分成员函数进行const修饰了。

#include <iostream>
#include <assert.h>
using namespace std;class Date {
public:Date(int year = 0, int month = 0, int day = 0);void Print();int GetMonthDay(int year, int month) 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;bool operator>=(const Date& d) const;Date& operator+=(int day);Date operator+(int day) const;Date& operator-=(int day);// d1 - 100Date operator-(int day);// d1 - d2;int operator-(const Date& d) const;// ++d1Date& operator++();// d1++Date operator++(int);Date& operator--();Date operator--(int);private:int _year;int _month;int _day;
};

四、取地址

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

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

class Date
{
public :Date* operator&(){return this ; }const Date* operator&()const{return this ;}
private :int _year ; // 年int _month ; // 月int _day ; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容。

五、[ ]运算符重载

class Array
{
public:int& operator[](int i){assert(i < 10);return _a[i];}const int& operator[](int i) const{assert(i < 10);return _a[i];}
private:int _a[10];int _size;
};void Func(const Array& aa)
{for (int i = 0; i < 10; ++i){//aa[i]++;cout << aa[i] << " ";}
}

首先,我们来看Array类:

  • Array类定义了一个私有的整型数组_a,大小为10,以及一个私有的整型变量_size

  • Array类重载了[]运算符,这样我们就可以像使用普通数组一样使用Array类的对象。

  • operator[]函数有两个版本,一个是非常量版本,一个是常量版本。非常量版本返回一个可修改的引用,常量版本返回一个不可修改的常量引用。

  • operator[]函数中,使用了assert函数来确保索引i小于10,防止数组越界。

然后,我们来看Func函数:

  • Func函数接受一个Array类的常量引用作为参数。因为参数是常量引用,所以我们不能在函数中修改参数的值。

  • Func函数中,有一个循环,循环变量i从0遍历到9。在循环体中,首先注释掉了aa[i]++,这是因为aa是一个常量引用,我们不能修改它的值。然后,使用cout打印出aa[i]的值,然后打印一个空格。

举个例子,如果我们创建一个Array类的对象a,并初始化_a数组为0到9,然后调用Func(a),那么控制台上会打印出0 1 2 3 4 5 6 7 8 9

相关文章:

C++类与对象(5)—流运算符重载、const、取地址

目录 一、流输出 1、实现单个输出 2、实现连续输出 二、流输入 总结&#xff1a; 三、const修饰 四、取地址 .取地址及const取地址操作符重载 五、[ ]运算符重载 一、流输出 1、实现单个输出 创建一个日期类。 class Date { public:Date(int year 1, int month 1,…...

Vue框架学习笔记——事件修饰符

文章目录 前文提要事件修饰符prevent&#xff08;常用&#xff09;stop&#xff08;不常用&#xff09;事件冒泡stop使用方法三层嵌套下的stop三层嵌套看出的stop&#xff1a; once&#xff08;常用&#xff09;capture&#xff08;不常用&#xff09;self&#xff08;不常用&a…...

嵌入式虚拟机原理

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…...

AMESim|Make failed:Unable to create an excutable for the system

最近在AMESIM与MATLAB进行联合仿真的时候遇到如下问题&#xff1a; Make failed:Unable to create an excutable for the system. 看了网上的解决办法如下 配置环境变量重装AMESIM&#xff0c;有顺序要求&#xff0c;首先是VS&#xff0c;然后是AMESIM与MATLAB。在AMESIM安装…...

OpenHarmony之NAPI框架介绍

张志成 诚迈科技高级技术专家 NAPI是什么 NAPI的概念源自Nodejs&#xff0c;为了实现javascript脚本与C库之间的相互调用&#xff0c;Nodejs对V8引擎的api做了一层封装&#xff0c;称为NAPI。可以在Nodejs官网&#xff08;https://nodejs.org/dist/latest-v20.x/docs/api/n-api…...

计算机网络之网络层

一、概述 主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 1.1网络引入的目的 从7层结构上看&#xff0c;网络层下是数据链路层 从4层结构上看&#xff0c;网络层下面是网络接口层 至少我们看到的网络层下面是以太网 以太网解决了什么问题&#xff1f; 答…...

【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…...

浅谈电力设备智能无线温度检测系统

安科瑞 华楠 摘要&#xff1a;在长期工作中&#xff0c;由于设备基础变化、温湿度变化、严重超负荷运行、触点氧化等原因造成的电力设备压接不紧&#xff0c;触头接触部分发生改变。终导致接触电阻增大&#xff0c;造成巨大的风险隐患。本系统将通过无线测温的方式&#xff0c…...

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二)

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二) mp4媒体流数据 #include <ros/ros.h> #include <signal.h> #include <sensor_msgs/Image.h> #include <message_filters/subscriber.h> #include <message_filters/synchroniz…...

LeetCode [简单] 283. 移动零

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 快慢指针&…...

深信服实验学习笔记——nmap常用命令

文章目录 1. 主机存活探测2. 常见端口扫描、服务版本探测、服务器版本识别3. 全端口&#xff08;TCP/UDP&#xff09;扫描4. 最详细的端口扫描5. 三种TCP扫描方式 1. 主机存活探测 nmap -sP <靶机IP>-sP代表 2. 常见端口扫描、服务版本探测、服务器版本识别 推荐加上-v参…...

面试:Kubernetes相关问题

文章目录 Kubernetes的工作流程可以分为以下几个步骤&#xff1a;简述Kubernetes中的Deployment、StatefulSet、DaemonSet的区别在Kubernetes中&#xff0c;如何进行存储管理在Kubernetes中&#xff0c;如何实现滚动升级和回滚在Kubernetes中&#xff0c;如何进行日志和监控的管…...

Go 本地搭建playground

搭建go playground 的步骤 1、安装docker 如果你使用的Ubuntu&#xff0c;docker的安装步骤可以参见这里&#xff0c;这是我之前写的在Ubuntu18.04下安装fabric&#xff0c;其中有docker的安装步骤&#xff0c;这里就不再赘述了。 CentOS下安装docker的&#xff0c;可以参见…...

Mybatis-plus常见标签

1.< 代表小于号&#xff08;<&#xff09;2.> 代表大于号&#xff08;>&#xff09;3.&le; 代表小于或等于符号&#xff08;≤&#xff09;4.&ge; 代表大于或等于符号&#xff08;≥&#xff09;5.< 代表<6.> 代表>7.ne; 代表不等于 <>8…...

一致性 Hash 算法 Hash 环发生偏移怎么解决

本篇是对文章《一文彻底读懂一致性哈希算法》的重写&#xff0c;图文并茂&#xff0c;篇幅较长&#xff0c;欢迎阅读完提供宝贵的建议&#xff0c;一起提升文章质量。如果感觉不错不要忘记点赞、关注、转发哦。原文链接&#xff1a; 《一文彻底读懂一致性Hash 算法》 通过阅读本…...

Javaweb之Vue组件库Element的详细解析

4 Vue组件库Element 4.1 Element介绍 不知道同学们还否记得我们之前讲解的前端开发模式MVVM&#xff0c;我们之前学习的vue是侧重于VM开发的&#xff0c;主要用于数据绑定到视图的&#xff0c;那么接下来我们学习的ElementUI就是一款侧重于V开发的前端框架&#xff0c;主要用…...

IBM X3650M4安装ESXI6.5卡在/lsl_mr3.v00

环境&#xff1a;IBM X3650M4服务器双盘配置raid1&#xff0c;通过rufus制作启动U盘&#xff0c;安装VMware Vsphere 5.5系统 问题&#xff1a;卡在/lsi_mr3.v00界面无法往下运行&#xff08;两台配置一样的机器遇到同样的问题&#xff09; 解决方案&#xff1a; 直接在U盘根…...

【Python3】【力扣题】338. 比特位计数

【力扣题】题目描述&#xff1a; 题解&#xff1a;从0到n的整数&#xff0c;逐一统计二进制中1的个数&#xff0c;记录在一个新列表中。 【Python3】代码&#xff1a; 1、解题思路&#xff1a;Python函数。 知识点&#xff1a;bin(...)&#xff1a;转为二进制字符串&#xff…...

Lubuntu 23.10用户可使用LXQt 1.4桌面

导读在众多 Lubuntu 用户的要求下&#xff0c;Lubuntu 开发人员决定将 LXQt 1.4 桌面环境向后移植到最新的 Lubuntu 23.10 &#xff08;Mantic Minotaur&#xff09; 版本。 是的&#xff0c;您没看错&#xff0c;您现在可以使用官方的 Lubuntu Backports PPA&#xff08;个人软…...

语音识别入门——常用软件及python运用

工具以及使用到的库 ffmpegsoxaudacitypydubscipylibrosapyAudioAnalysisplotly 本文分为两个部分&#xff1a; P1&#xff1a;如何使用ffmpeg和sox处理音频文件 P2&#xff1a;如何编程处理音频文件并执行基本处理 P1 处理语音数据——命令行方式 格式转换 ffmpeg -i video…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

用递归算法解锁「子集」问题 —— LeetCode 78题解析

文章目录 一、题目介绍二、递归思路详解&#xff1a;从决策树开始理解三、解法一&#xff1a;二叉决策树 DFS四、解法二&#xff1a;组合式回溯写法&#xff08;推荐&#xff09;五、解法对比 递归算法是编程中一种非常强大且常见的思想&#xff0c;它能够优雅地解决很多复杂的…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...