C++11QT复习 (四)

Day6-1 输入输出流运算符重载(2025.03.25)

1. 拷贝构造函数的调用时机
2. 友元2.1 友元函数
3. 输入输出流运算符重载3.1 关键知识点3.2 代码3.3 关键问题3.4 完整代码
4. 下标访问运算符 `operator[]`4.1 关键知识点4.2 代码
5. 函数调用运算符 `operator()`5.1 关键知识点5.2 代码5.3 示例5.4 完整代码
6. 总结

1. 回顾

1.1 拷贝构造函数的调用时机

拷贝构造函数在以下情况会被调用:

  1. 对象初始化

    • 当用一个已经存在的对象去初始化一个刚刚创建的对象时,调用拷贝构造函数。
    • 例:
      Complex c1(1, 2);
      Complex c2 = c1; // 调用拷贝构造函数
      
  2. 函数参数传递

    • 当形参与实参都是对象时,在函数调用时会调用拷贝构造函数。
    • 例:
      void func(Complex c) { }
      func(c1); // 形参与实参结合时调用拷贝构造函数
      
  3. 函数返回对象

    • 当函数返回一个对象时,可能调用拷贝构造函数(但现代 C++ 编译器会尝试优化此过程,如返回值优化 RVO)。
    • 例:
      Complex func() {Complex c(3, 4);return c; // 可能调用拷贝构造函数
      }
      

2. 友元

2.1 友元函数
  • 友元函数可以访问类的私有成员。
  • 友元声明可以出现在类的 publicprotectedprivate 部分,不影响其权限。
  • 友元关系是单向的、不可传递的
    • 单向:如果 AB 的友元,B 并不会自动成为 A 的友元。
    • 不可传递:如果 AB 的友元,BC 的友元,A 并不会自动成为 C 的友元。

3. 输入输出流运算符重载

3.1 关键知识点
  1. operator<< 必须是友元函数

    • 由于 cout << c1; 左操作数是 std::ostream,不能修改 std::ostream,所以 operator<< 不能是 Complex 的成员函数。
  2. operator>> 不能是 const 成员函数

    • 因为 operator>> 需要修改对象的值,因此不能加 const
3.2 代码
std::ostream& operator<<(std::ostream& os, const Complex& rhs) {if (rhs._imag > 0) {os << rhs._real << " + " << rhs._imag << "i";} else if (rhs._imag == 0) {os << rhs._real;} else {os << rhs._real << " - " << -rhs._imag << "i";}return os;
}std::istream& operator>>(std::istream& is, Complex& rhs) {std::cout << "请输入复数的实部和虚部:" << std::endl;is >> rhs._real >> rhs._imag;return is;
}
3.3 关键问题
  • 为什么 operator<<operator>> 的返回值是 std::ostream&std::istream&

    • 这样可以实现连续输入输出
      cout << c1 << c2 << endl;  // 连续输出
      cin >> c1 >> c2;           // 连续输入
      
  • 为什么 operator<<ostream& 参数不能去掉 &

    • 因为 ostream 的拷贝构造函数已被 delete,不能复制 ostream 对象。
3.4 完整代码
#include <iostream>
#include <limits.h>
#include <ostream>using namespace std;//复数
class Complex
{friend Complex operator*(const Complex& lhs, const Complex& rhs);//Complex operator/(const Complex& rhs) const;
public:Complex(double r = 0, double i = 0): _real(r), _imag(i){cout << "Complex(double r = 0, double i = 0)" << endl;}~Complex(){cout << "~Complex()" << endl;}void display() const{if (_imag > 0){cout << _real << " + " << _imag << "i" << endl;}else if (_imag == 0){cout << _real << endl;}else{cout << _real << " - " << -_imag << "i" << endl;}}//成员函数,operator输出运算符重载 //cout << c1 << endl; //第一个参数cout ,第二个参数c1//std::ostream& operator<<(std::ostream& os, const Complex& rhs); //error!隐含this指针//对于输出流运算符函数而言,不能写成成员函数的形式,因为违背了运算符重载的原则,不能改变操作数的顺序//std::ostream& operator<<(std::ostream& os);//error!this指针在参数列表的第一个位置/*友元函数可以放在类的 任何位置(public / protected / private),不影响其功能。从代码规范角度,建议统一放在类定义的开头或结尾,以提高可读性。友元关系是单向的,且不具备传递性(即类 A 的友元函数不会自动成为类 B 的友元)。*/friend std::ostream& operator<<(std::ostream& os, const Complex& rhs);//输入流运算符重载friend std::istream& operator>>(std::istream& is, Complex& rhs);private:double _real;double _imag;
};
//问题1 :参数列表中ostrream的引用符号&能不能去掉?
//解答1 :不能去掉因为形参"os"和实参"cout"结合的时候会满足拷贝构造函数的调用时机,但是ostream中的拷贝构造函数已被delete//问题2 : 函数返回类型中的引用符号&能不能删除?
//解答2 : 不能取掉,因为return os,返回类型满足拷贝构造函数的调用时机3//注释:basic_ifstream( const basic_ifstream& rhs ) = delete; (7)	(since C++11)
//	   basic_ofstream( const basic_ofstream& rhs ) = delete; (7)	(since C++11)
//ifstram 和 ofstream 的拷贝构造函数已经从C++11开始删除了
std::ostream& operator<<(std::ostream& os, const Complex& rhs)
{cout << "std::ostream& operator<<(std::ostream& os, const Complex& rhs)" << endl;if (rhs._imag > 0){os << rhs._real << " + " << rhs._imag << "i" << endl;}else if (rhs._imag == 0){os << rhs._real << endl;}else{os << rhs._real << " - " << -rhs._imag << "i" << endl;}return os;
}void readDouble(std::istream& is, double& rhs)
{while (is >> rhs, !is.eof()){if (is.bad()){std::cerr << "istream is bad" << endl;return;}else if (is.fail()){is.clear();//重置流的状态is.ignore(std::numeric_limits<::std::streamsize>::max(), '\n');//清空缓冲区cout << "请注意:需要输入double类型的数据!";}else{cout << "rhs = " << rhs << endl;break;}}
}//输入流运算符重载
std::istream& operator>>(std::istream& is, Complex& rhs)//因为要修改rhs 的值,所以不能加const
{cout << "std::istream& operator>>(std::istream& is, Complex& rhs)" << endl;cout << "请分别输入复数的实部和虚部:" << endl;//is >> rhs._real >> rhs._imag;readDouble(is, rhs._real);readDouble(is, rhs._imag);return is;
}void testOutputOperator()
{Complex c1(1, 2);cout << "c1 = ";c1.display();cout << endl << endl;//cout << "c1 = " << c1.display();//为什么没有做输出流运算符重载之前上面的写法 不可行呢? 解答:二元“<<”: 没有找到接受“void”类型的右操作数的运算符(或没有可接受的转换)cout << "c1 = " << c1 << endl;cout << endl;Complex c2;cin >> c2;cout << "c2 = " << c2 << endl;
}int main(int argc, char* argv[])
{testOutputOperator();return 0;
}

4. 下标访问运算符 operator[]

4.1 关键知识点
  • operator[] 主要用于自定义数组类型,使得 obj[idx] 访问数组元素。
  • 返回值应为 T&,以保证能够修改数组内容。
  • 必须进行越界检查,以防止访问非法内存。
4.2 代码
char& CharArrar::operator[](size_t idx) {if (idx < _size) {return _data[idx];} else {static char charNull = '\0';return charNull;}
}

5. 函数调用运算符 operator()

5.1 关键知识点
  • 使对象像函数一样调用,称为仿函数(函数对象)
  • 可存储状态,例如调用次数。
5.2 代码
class FunctionObject {
public:int operator()(int x, int y) {++_cnt;return x + y;}int operator()(int x, int y, int z) {++_cnt;return x * y * z;}private:int _cnt = 0;
};
5.3 示例
FunctionObject fo;
cout << "fo(3, 4) = " << fo(3, 4) << endl;
cout << "fo(3, 4, 5) = " << fo(3, 4, 5) << endl;
5.4 完整代码
//bracket.h
#pragma once
#include <iostream>
#include <string.h>using namespace std;
class CharArrar
{
public:CharArrar(size_t sz = 10):_size(sz), _data(new char[_size]){cout << " CharArrar(size_t sz = 10)" << endl;}~CharArrar(){cout << "~CharArrar()" << endl;if (_data){delete _data;_data = nullptr;}}size_t size() const{return _size;}//下标访问运算符的重载//int arr[10] = { 1,2,3,4,5 };//arr.operator[](idx);char& operator[](size_t idx);
private:size_t _size;char* _data;
};

//bracket.cpp
#include "bracket.h"/*
要修复错误 C2106: “=”: 左操作数必须为左值,需要确保 CharArrar 类的
下标运算符 operator[] 返回一个可修改的左值。当前的 operator[] 声明
返回的是一个 char,这不是一个可修改的左值。我们需要将其修改为返回一个 char&。
*/
char& CharArrar::operator[](size_t idx)
{if (idx < _size){return _data[idx];}else{static char charNUll = '\0';//静态变量延长生命周期return charNUll;   //使得返回的实体生命周期比函数的生命周期长}
}//C++中优势:重载的下标访问运算符考虑了越界的问题
//引用什么时候需要加上?
//1.如果返回类型是类型的时候,可以减少拷贝构造函数的执行
//2.有可能需要一个左值(来调用右操作数),而不是拷贝后的右值、
//3.cout << "111" << c1 << endl << 10 << 1 << endl,像这种情况下连续使用的时候可以加上引用 

//parenthese.h
#pragma once
#include <iostream>using namespace std;class FunctionObject
{
public:int operator()(int x, int y);int operator()(int x, int y, int z);private:int _cnt;//记录被调用的次数(函数对象状态)
};

parenthese.cpp
#include <iostream>
#include "parenthese.h"using namespace std;int FunctionObject::operator()(int x, int y)
{++_cnt;cout << "int operator()(int x, int y)" << endl;return x + y;
}int FunctionObject::operator()(int x, int y, int z)
{cout << "int operator()(int x, int y,int z)" << endl;++_cnt;return x * y * z;
}

//main.cpp
#include <iostream>
#include "bracket.h"
#include "parenthese.h"using namespace std;int add(int x, int y)
{cout << "int add(int x,int y)" << endl;static int cnt = 0;++cnt;return x + y;
}void testFunctionObject()
{FunctionObject fo;int a = 3;int b = 4;int c = 5;//fo本质上是一个对象,但是他的使用cout << "fo(a,b) = " << fo(a, b) << endl;cout << "fo(a, b ,c) = " << fo(a, b, c) << endl;cout << endl;//正经的函数cout << "add(a, b) = " << add(a, b) << endl;
}void testCharArrar()
{//把字符串中的内容拷贝到CharArrayconst  char* pstr = "hello cpp";CharArrar ca(strlen(pstr) + 1);for(size_t idx = 0; idx != ca.size(); ++idx){//ca[idx] = pstr[idx];//上面和下面两条代码等价ca.operator[](idx) = pstr[idx];}for (size_t idx = 0; idx != ca.size(); ++idx){cout << ca[idx] << "  ";}cout << endl;
}int main(int argc, char** argv)
{testFunctionObject();testCharArrar();return  0;
}

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

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

相关文章

数仓架构告别「补丁」时代!全新批流一体 Domino 架构终结“批流缝合”

在数字化转型的浪潮中&#xff0c;企业对数据处理的需求日益复杂多变&#xff0c;传统的批处理和流处理架构已难以满足日益增长的性能和时效性要求。在此背景下&#xff0c;YMatrix CEO 姚延栋发布了深度文章《数仓架构告别「补丁」时代&#xff01;全新批流一体 Domino 架构终…

一文详解QT环境搭建:ubuntu20.4安装配置Qt5

随着软件开发技术的不断进步&#xff0c;跨平台应用程序的需求日益增长&#xff0c;开发者们面临着如何在不同操作系统之间保持代码的一致性和效率的问题。Qt作为一个成熟的跨平台C框架&#xff0c;在这方面提供了卓越的支持&#xff0c;不仅简化了GUI应用程序的创建过程&#…

安全+低碳+高效:Acrel-3000助力企业打造未来型电能管理体系-安科瑞黄安南

一 背景 电能因为方便传输、易于转换、便于控制等特性&#xff0c;成为广大企事业单位生产、办公最主要的能量来源。双碳背景下&#xff0c;由于电能清洁、高效、零排放的特点&#xff0c;能源消费侧将逐步以电代煤、以电代油、以电代气&#xff0c;形成以电为中心的能源消费体…

Docker 安装 RabbitMQ

以下是在Docker中安装RabbitMQ并实现配置、数据、日志文件映射的完整步骤。 步骤 1&#xff1a;创建本地目录结构 # 创建配置、数据、日志目录 mkdir -p /root/docker/rabbitmq/{conf,data,logs}# 目录结构说明&#xff1a; # - conf: 存放自定义配置文件 # - data: 持久化存储…

SAP-ABAP:SAP数据集成全场景技术指南(BAPI、RFC、IDOC、BATCHJOB、ODATA、WEBSERVICE):从实时交互到批量处理

SAP数据集成全场景技术指南:从实时交互到批量处理 #mermaid-svg-hpPMerJYUerla0BJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hpPMerJYUerla0BJ .error-icon{fill:#552222;}#mermaid-svg-hpPMerJYUerla0BJ .er…

运维规则之总结(Summary of Operation and Maintenance Rules)

运维规则之总结 在运维领域&#xff0c;经验和流程往往决定了系统的稳定性与可靠性。一个运维人&#xff0c;总结出了以下10条运维规则&#xff0c;涵盖了从基础管理到高级策略的全面内容&#xff0c;旨在帮助运维人员更好地应对各种挑战&#xff0c;确保系统的平稳运行。 1.…

⑦(ACG-网络配置)

网络配置是指对计算机网络的各种参数进行设置和调整&#xff0c;以实现网络正常运行和高效通信。网络配置包括多方面的内容&#xff0c;常见的配置包括&#xff1a; 1. IP地址设置&#xff1a;IP地址是设备在网络中的身份标识&#xff0c;设置IP地址是网络配置的基础&#xff…

Redis学习二

Redis和数据库数据一致性问题 Redis作为缓存分两种情形 只读缓存, 只读缓存无需考虑数据更新问题, Redis中有则返回Redis中的数据, Redis无则查询数据库读写缓存 同步直写策略异步缓写策略 数据读取流程: 正常回写Redis代码流程: public Object getDataById(String id) {…

深入理解 Linux 文件权限:从 ACL 到扩展属性,解剖底层技术细节与命令应用

Linux 以其强大而精密的文件权限和属性管理机制著称&#xff0c;这一体系不仅是系统安全的关键基石&#xff0c;还为灵活性和扩展性提供了坚实支撑。从传统的九位权限模型到访问控制列表&#xff08;ACL&#xff09;、扩展文件属性&#xff08;Extended Attributes&#xff09;…

剑指Offer35- - 链表

1. 题目描述 这题题意感觉说的不是很清楚&#xff0c;容易让人产生歧义&#xff01;其实题意很简单&#xff0c;给你一个链表 head&#xff0c;你深拷贝它&#xff0c;然后返回即可&#xff0c;注意不能修改原链表 /* // Definition for a Node. class Node { public:int val;N…

C 语言常用关键字详解:static、const、volatile

C 语言常用关键字详解&#xff1a;static、const、volatile 文章目录 C 语言常用关键字详解&#xff1a;static、const、volatile1. static 关键字1.1 用于局部变量示例&#xff1a; 1.2 用于全局变量示例&#xff1a; 1.3 用于函数示例&#xff1a; 2. const 关键字2.1 用于局…

Centos7本地部署阿里Qwen2-7B模型

1.从hagging face下载模型 2.把下载的模型文件&#xff0c;放到/usr/local/Qwen2-7B目录下 3.创建虚拟环境&#xff0c;安装依赖 1.环境安装 sudo yum update -y sudo yum install -y python3 python3-pip git 2.创建虚拟环境并激活 python3 -m venv qwen2_env source qwen2_…

群晖监控套件通过ONVIF协议添加海康摄像头

1. 首先登录录像机 通道管理 找到每个摄像头的IP地址 2. 登录某个摄像头 配置 3. 添加用户名&#xff08;注意不能是admin&#xff09; 设置账户密码 用户类型选管理员 4. 群晖里面添加摄像头&#xff0c;自动搜索&#xff0c;添加刚刚那个IP的摄像头 5. 验证…

【C++】 —— 笔试刷题day_8

一、求最小公倍数 题目解析 题目很简单&#xff0c;给定两个数a和b求它们的最小公倍数。 算法思路 对于求两个数的最小公倍数问题&#xff0c;想必已经非常熟悉了&#xff1b; 在之前学校上课时&#xff0c;记得老师提起过&#xff0c;最小公倍数 两个数的乘积 除以最大公约数…

MTK Android12-Android13 设置系统默认语言

Android 系统&#xff0c;默认语言 文章目录 需求&#xff1a;场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…

系统如何查找文件?inode号又是什么?

下面分别详细解释您提到的三个问题&#xff1a; “文件系统怎么定位文件”、“inode 是什么”、“为什么删除后还可能被占用”。 一、文件系统怎么定位文件 1.1 目录与文件名并不直接存储文件数据 在常见的 Unix/Linux 文件系统&#xff08;如 ext4、xfs&#xff09;或类似的…

05-SpringBoot3入门-整合SpringMVC(配置静态资源、拦截器)

1、说明 在01-SpringBoot3入门-第一个项目-CSDN博客中&#xff0c;其实就已经整合了SpringMVC。下面讲解怎么配置静态资源和拦截器 2、配置静态资源 命名&#xff1a;static&#xff08;文件夹&#xff09; 位置&#xff1a;src/main/resources 编写一个html文件 访问 http:/…

Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测

聚划算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测 目录 聚划算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 聚划算&#xff01;Tran…

树莓派浏览器配置全解析:从轻量系统到网页应用平台

树莓派&#xff08;Raspberry Pi&#xff09;不仅是嵌入式开发的入门利器&#xff0c;也因其低成本和强大的社区支持而成为物联网、数字标牌、教育培训等领域的热门平台。在很多应用中&#xff0c;运行一个浏览器并作为 Web 前端展示、操作或交互的能力显得尤为关键。 但在资源…

初识Qt(一)

本文部分ppt、视频截图原链接&#xff1a;萌马工作室的个人空间-萌马工作室个人主页-哔哩哔哩视频 1. Qt是什么&#xff1f; Qt是一个跨平台的C应用程序开发框架&#xff0c;它既为图形用户界面(GUI)程序开发提供了强大支持&#xff0c;也能用于开发非GUI的控制台程序、服务端…