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

【c++面向对象编程】第22篇:输入输出运算符重载:<< 与 >> 的友元实现

目录一、为什么不能是成员函数二、标准写法两步法第1步在类中声明友元函数第2步实现全局函数三、为什么要返回引用支持链式输出正确 vs 错误示例四、为什么需要友元能否不用友元五、输入运算符的注意事项1. 参数是 non-const 引用2. 需要处理输入错误3. 处理空白字符六、完整例子支持多种格式的输入输出七、输入输出的链式原理链式输出链式输入八、常见错误1. 忘记返回引用2. 忘记加 const输出运算符3. 输入运算符没有处理错误状态4. 把 和 声明为成员函数九、这一篇的收获一、为什么不能是成员函数尝试把operator写成成员函数cppclass Point { int x, y; public: Point(int a, int b) : x(a), y(b) {} // ❌ 如果作为成员函数 ostream operator(ostream os) const { os ( x , y ); return os; } }; int main() { Point p(3, 4); p cout; // 要这样调用太别扭了 // 我们想要的是 cout p }成员函数要求左侧操作数是Point对象所以只能写成p cout。这不符合直觉也不符合标准库的惯例。正确的方式全局函数左侧是ostream右侧是const Point。二、标准写法两步法第1步在类中声明友元函数cppclass Point { int x, y; public: Point(int a, int b) : x(a), y(b) {} // 声明友元注意不是成员函数 friend ostream operator(ostream os, const Point p); friend istream operator(istream is, Point p); };第2步实现全局函数cppostream operator(ostream os, const Point p) { os ( p.x , p.y ); return os; // 返回os引用支持链式输出 } istream operator(istream is, Point p) { // 假设输入格式 (3,4) 或 3 4 char ch; is ch; // 读取 ( 或数字 if (ch () { is p.x ch p.y ch; // 读取 x , y ) } else { is.putback(ch); // 不是括号把字符放回去 is p.x p.y; } return is; // 返回is引用支持链式输入 }三、为什么要返回引用支持链式输出cppPoint p1(1,2), p2(3,4); cout p1 , p2 endl;编译器把这句话翻译成cpp((cout p1) , ) p2;如果operator返回的是ostreamcout的引用那么(cout p1)的结果还是 cout可以继续 , 。如果返回void或复制了一个临时对象链式调用就会失败。正确 vs 错误示例cpp// ✅ 正确返回引用 ostream operator(ostream os, const Point p) { os p.x , p.y; return os; } // ❌ 错误返回void不能链式 void operator(ostream os, const Point p) { ... } // ❌ 错误返回值不是引用链式时操作的是临时对象 ostream operator(ostream os, const Point p) { ... }四、为什么需要友元能否不用友元如果类提供了getX()/getY()等公共接口可以不用友元cppclass Point { int x, y; public: Point(int a, int b) : x(a), y(b) {} int getX() const { return x; } int getY() const { return y; } }; // 不用友元 ostream operator(ostream os, const Point p) { os ( p.getX() , p.getY() ); return os; }但通常用友元更好性能避免函数调用开销虽然编译器可能内联语义是“输出对象状态”属于类的外部接口友元表达这种紧密关系一致性标准库对自定义类型的通常通过友元实现如complex结论可以用 getter但友元更直接、更高效、更符合惯例。五、输入运算符的注意事项1. 参数是 non-const 引用cppistream operator(istream is, Point p) // p 要能被修改不能是const Point因为要修改对象。2. 需要处理输入错误cppistream operator(istream is, Point p) { int x, y; if (is x y) { // 读取成功才赋值 p.x x; p.y y; } return is; }更好的做法是设置failbit或抛出异常但初学阶段先保证基本功能。3. 处理空白字符默认会跳过空白空格、换行、制表符通常不需要特殊处理。六、完整例子支持多种格式的输入输出cpp#include iostream #include string #include sstream using namespace std; class Complex { private: double real; double imag; public: Complex(double r 0, double i 0) : real(r), imag(i) {} // 友元声明 friend ostream operator(ostream os, const Complex c); friend istream operator(istream is, Complex c); }; // 输出支持两种格式 // 虚数部分为正34i // 虚数部分为负3-4i // 虚数为03 ostream operator(ostream os, const Complex c) { if (c.imag 0) { os c.real; } else { os c.real; if (c.imag 0) os ; os c.imag i; } return os; } // 输入支持多种格式 // 34i 3-4i 3 4 34i (3,4) istream operator(istream is, Complex c) { double r 0, i 0; char ch 0; // 尝试读取一个数字 is r; if (!is) return is; // 读取失败 // 检查下一个字符 is ch; if (ch || ch -) { // 格式34i 或 3-4i double imagPart; is imagPart; if (ch -) imagPart -imagPart; is ch; // 读取 i if (ch ! i) is.setstate(ios::failbit); i imagPart; } else if (ch i) { // 格式3i虚数部分为3实数部分为0 i r; r 0; } else { // 格式3 4只有两个数字 is.putback(ch); is i; } if (is) { c.real r; c.imag i; } return is; } int main() { Complex c1(3, 4); Complex c2(5, -2); Complex c3(7, 0); cout c1 c1 endl; cout c2 c2 endl; cout c3 c3 endl; cout \n链式输出测试: c1 | c2 | c3 endl; cout \n请输入复数 (格式: abi 或 a b 或 a): ; Complex c4; cin c4; cout 你输入了: c4 endl; // 演示错误处理 cout \n尝试输入无效格式: ; Complex c5; cin c5; if (cin.fail()) { cout 输入失败格式错误 endl; cin.clear(); // 清除错误状态 } return 0; }运行示例textc1 34i c2 5-2i c3 7 链式输出测试: 34i | 5-2i | 7 请输入复数 (格式: abi 或 a b 或 a): 2-3i 你输入了: 2-3i七、输入输出的链式原理链式输出cppcout p1 , p2 endl;展开为cpp(((cout p1) , ) p2) endl;每一步返回cout本身所以可以继续。链式输入cppcin p1 p2;展开为cpp(cin p1) p2;先执行cin p1返回cin再执行cin p2。八、常见错误1. 忘记返回引用cppostream operator(ostream os, const Point p) { // 返回值不是引用 return os; // 复制了一个ostream通常不可复制 }2. 忘记加 const输出运算符cppostream operator(ostream os, Point p) { // 应该用 const Point // 不能输出 const 对象 }3. 输入运算符没有处理错误状态cppistream operator(istream is, Point p) { is p.x p.y; // 如果读取失败p可能处于不一致状态 return is; }更好的做法先读入临时变量成功后再赋值。4. 把和声明为成员函数cppclass Point { ostream operator(ostream os) const; // ❌ 调用时会是 p cout };九、这一篇的收获你现在应该理解和不能是成员函数必须是全局函数为了访问私有成员通常需要友元也可以用 public getter必须返回ostream/istream才能支持链式操作输入运算符要处理错误避免对象处于不一致状态输出运算符参数要用const因为输出不应修改对象 小作业为第21篇的Rational类完善和运算符。输出格式如3/4或5整数时只输出分子。支持3/4、3、1 2空格分隔等多种格式。下一篇预告第23篇《自增/自减运算符重载前置与后置的区别》——前置obj和后置obj如何区分它们的效率差异在哪为什么后置通常用前置来实现下篇解答。

相关文章:

【c++面向对象编程】第22篇:输入输出运算符重载:<< 与 >> 的友元实现

目录 一、为什么不能是成员函数? 二、标准写法(两步法) 第1步:在类中声明友元函数 第2步:实现全局函数 三、为什么要返回引用? 支持链式输出 正确 vs 错误示例 四、为什么需要友元?能否不…...

基于CircuitPython与蓝牙BLE的交互式电子糖果心制作指南

1. 项目概述:一个可交互的蓝牙电子糖果心 情人节期间,那些印着“BE MINE”、“HUG ME”等短句的糖果心(Conversation Hearts)总是能传递简单而直接的情感。你有没有想过,如果能亲手制作一个可以随时改变文字和颜色的电…...

微软UFO项目:统一AI模型调用的抽象层设计与工程实践

1. 项目概述:当“统一”成为AI开发的新范式最近在折腾大模型应用开发的朋友,可能都绕不开一个痛点:模型太多,工具链太杂。想用闭源的GPT-4处理文本,用开源的Llama搞本地推理,再用DALL-E 3生成图片&#xff…...

接手遗留系统第一周,我做了三件事,团队从此不再怕改老代码

刚跳槽到新公司,技术总监在入职谈话时递给我一杯咖啡,语气沉重地说:“我们最核心的交易系统已经跑了八年,负责它的老张去年离职了。现在整个团队没人敢动里面的代码,每次改需求都像在拆炸弹。”他停顿了一下&#xff0…...

【建筑学研究降维打击】:为什么顶尖事务所已禁用传统文献管理?NotebookLM智能溯源+跨语言规范比对实战拆解

更多请点击: https://intelliparadigm.com 第一章:NotebookLM建筑学研究辅助的范式革命 NotebookLM 作为 Google 推出的基于用户自有文档的 AI 助手,正悄然重塑建筑学研究的方法论边界。它不再依赖通用知识库的泛化回答,而是以建…...

代码审查时最该关注的不是语法,而是这五个“坏味道”

“这段代码能跑,但总觉得哪里不对劲。”如果你在审查代码时有过这种感觉,说明你已经嗅到了代码的坏味道。作为软件测试从业者,我们往往比开发人员更早感受到坏味道带来的痛苦——一个看似简单的变更导致回归测试大面积失败,一个边…...

书成紫微动,律定凤凰驯:从无心创作到天命显化的海棠山铁哥之路

书成紫微动,律定凤凰驯。 ——南北朝庾信一、千古谶语,千年未解诗句天道逻辑千年误读书成紫微动先著书立道,撼动文脉附会玄学,强行造神律定凤凰驯再定规立序,祥瑞归宁脑会剧情,虚妄狂欢 无人真正落地&#…...

我们团队的技术债已经堆成山,我用这四步说服老板给时间重构

在软件测试的日常工作中,我们或许是技术债最敏锐的感知者。每一次回归测试的漫长等待,每一个在“祖传代码”上小心翼翼打补丁的深夜,每一份因环境不稳定而飘红的测试报告,都在无声地控诉着那座压得团队喘不过气的“屎山”。然而&a…...

基于RAG与LLM的法律合规助手:架构、实现与工程实践

1. 项目概述:一个AI驱动的法律合规助手最近在GitHub上看到一个挺有意思的项目,叫ai-legal-compliance-assistant。光看名字,很多朋友可能觉得这又是一个蹭AI热点的“玩具”,或者是一个简单的规则匹配工具。但当我深入研究了它的架…...

ARM PMUv3架构详解与性能监控实战

1. ARM PMUv3架构概述 性能监控单元(Performance Monitor Unit, PMU)是现代处理器中用于硬件性能分析的关键组件。作为ARMv8架构的标准组成部分,PMUv3通过事件计数器和配置寄存器实现了对微架构事件的监测能力。在实际开发中,我们经常需要利用PMU来定位性…...

如何提升宝塔面板文件管理效率_使用SSH命令与Web端结合.txt

...

Proxima向量检索库:硬件优化与量化技术实战解析

1. 项目概述:一个为现代开发者打造的“近邻”代码库 最近在GitHub上看到一个挺有意思的项目,叫“Zen4-bit/Proxima”。乍一看这个标题,可能会有点摸不着头脑。“Zen4-bit”像是一个用户名或者某种架构的代号,而“Proxima”则让人联…...

LaTeX-PPT:3分钟学会在PowerPoint中快速插入专业数学公式的终极指南

LaTeX-PPT:3分钟学会在PowerPoint中快速插入专业数学公式的终极指南 【免费下载链接】latex-ppt Use LaTeX in PowerPoint 项目地址: https://gitcode.com/gh_mirrors/la/latex-ppt 你是否曾经在PowerPoint中为编辑复杂的数学公式而头疼?手动调整…...

开源破产法律实务知识库:构建结构化办案指南与协作平台

1. 项目概述:一个破产法律实务的开源知识库最近在整理过往的破产案件卷宗时,我一直在思考一个问题:如何将那些零散、重复但又至关重要的法律文书、办案流程和实务要点,系统地沉淀下来,形成一套可以随时查阅、迭代更新的…...

【架构实战】百万级Excel数据导入的“坑”与“填坑”指南(上):痛点剖析与破局利器 EasyExcel

前言大家好,这里是程序员阿亮!今天来给大家讲解一下在传统企业中报表和数据处理业务非常常见的工具-Excel在后端的使用和场景!引言:从一个看似简单的需求说起在日常的 B2B 业务、ERP 系统或者后台管理系统中,“Excel 导…...

大语言模型本地化部署利器:Synaptic-Link 模型文件管理工具详解

1. 项目概述与核心价值最近在折腾一些AI相关的本地化部署和模型管理,发现一个挺有意思的项目,叫dlxeva/synaptic-link。乍一看这个名字,可能有点摸不着头脑,“突触链接”?听起来像是神经科学或者生物信息学的东西。但如…...

交通事故车辆受损情况数据集分享(适用于YOLO系列深度学习分类检测任务)

交通事故车辆受损情况数据集分享(适用于YOLO系列深度学习分类检测任务) 源码下载链接:https://pan.baidu.com/s/1zYLg1EOwHB-HTBlxQr4w7A?pwdyhmd 提取码:yhmd前言 随着道路交通量的不断增加,交通事故的发生频率也呈现上升趋势。事故发生后&…...

基于视觉大模型的GUI自动化:从原理到实践

1. 项目概述:当GUI自动化遇见视觉大模型 最近在折腾自动化测试和RPA(机器人流程自动化)的时候,我遇到了一个老生常谈但又极其棘手的问题:如何稳定、高效地识别和操作那些没有标准控件标识的图形界面元素?传…...

AI编程也开始“贵价提速”?Cursor上线Opus极速模式,官方却劝你:别开,真不值!

前言各位码农老铁们,最近有没有感觉写代码像在开手动挡老爷车——油门踩到底,AI还在“思考人生”?别急,Cursor贴心地给你装了个“涡轮增压”:Claude Opus 4.7 Fast mode,号称速度拉满、输出飞起&#xff01…...

ARM ETMv4跟踪寄存器架构与调试实践

1. ARM ETMv4 跟踪寄存器架构概述ARM嵌入式跟踪宏单元(ETM)是处理器调试架构中的关键组件,ETMv4作为其第四代架构,提供了更强大的指令和数据跟踪能力。与传统的断点调试不同,ETM采用实时跟踪技术,能够在不中断处理器运行的情况下&…...

Bash脚本集成AI:实现智能运维自动化与决策增强

1. 项目概述:当Bash脚本遇见AI,自动化运维的“智能大脑”如果你和我一样,是个常年和Linux服务器、运维脚本打交道的“老运维”或开发者,那你肯定对Bash脚本又爱又恨。爱的是它的直接、高效,几行命令就能串联起复杂的系…...

OpenClaw AVP:构建统一音视频协议栈,实现多协议流媒体处理

1. 项目概述:一个面向音视频处理的协议栈最近在整理一些音视频项目时,又翻到了avp-protocol/openclaw-avp这个仓库。对于从事流媒体、实时通信或者音视频编解码开发的工程师来说,看到avp这个缩写,第一反应多半是 “Audio-Video Pr…...

纯文本CRM:用Markdown与Git构建极简客户关系管理系统

1. 项目概述与核心价值最近在开源社区里,我注意到一个名为anthroos/plaintext-crm的项目,它提出了一种非常规的客户关系管理(CRM)思路。简单来说,这个项目主张用纯文本文件(如 Markdown、TXT)来…...

声明式应用编排框架Planifest:云原生时代应用交付新范式

1. 项目概述:一个面向未来的声明式应用编排框架如果你和我一样,在云原生和自动化运维领域摸爬滚打了几年,就会深刻体会到“编排”这个词的分量。从早期的Shell脚本,到Ansible、Terraform,再到Kubernetes的YAML海洋&…...

基于计算机视觉的屏幕内容智能识别与自动化实践

1. 项目概述:当屏幕成为你的“眼睛”最近在折腾一个挺有意思的项目,我把它叫做“Screen Vision”,直译过来就是“屏幕视觉”。这名字听起来有点玄乎,但核心想法其实很直接:让计算机程序能像人一样,“看懂”…...

从Excel到数据库:用Pandas Timestamp统一你的时间数据(pd.to_datetime实战解析)

从Excel到数据库:用Pandas Timestamp统一你的时间数据(pd.to_datetime实战解析) 在数据工程领域,时间数据的标准化处理往往是ETL流程中最容易被低估的痛点。当Excel表格中的"2023/1/15"遇上数据库里的"15-JAN-23&q…...

AI智能体长期记忆架构:构建Agent Shadow Brain解决上下文限制

1. 项目概述:当AI智能体拥有一个“影子大脑”最近在AI智能体开发领域,一个名为“Agent Shadow Brain”的项目引起了我的注意。这个项目由开发者theihtisham发起,其核心思想是为大型语言模型驱动的智能体配备一个独立的、持续运行的“影子大脑…...

RFSoC开发避坑指南:手把手教你理解并配置RF数据转换器的核心结构体(以XRFdc为例)

RFSoC开发实战:深度解析XRFdc结构体配置与避坑策略 第一次打开xrfdc.h头文件时,面对密密麻麻的结构体定义,我的鼠标滚轮不由自主地滑动了三分钟才看完所有内容。作为曾经在RFSoC项目上踩过无数坑的开发者,我完全理解那种面对数十个…...

Godot集成CEF:用Web技术构建高性能跨平台桌面应用

1. 项目概述:一个被低估的桌面应用开发利器 如果你正在寻找一个能让你用熟悉的Web技术(HTML、CSS、JavaScript)来构建高性能、跨平台桌面应用的工具,并且对Electron的臃肿和资源占用感到头疼,那么你很可能已经听说过C…...

当深度学习赋能异步电机矢量控制:从模型优化到性能跃迁

1. 异步电机矢量控制的传统挑战 我第一次接触异步电机矢量控制是在2015年做工业机器人项目时。当时为了调试一个简单的速度环,整整花了两周时间反复调整PI参数。这种经历让我深刻体会到传统控制方法的局限性——就像用螺丝刀修理精密手表,虽然最终能调好…...