C++学习日记 | LAB 10 运算符重载与友元函数

资料来源:南科大 于仕琪 C/C++ Program Design

LINK:CPP/week10 at main · ShiqiYu/CPP · GitHub


一、本节内容

       本节首先以一个例子具体演示和回顾操作符重载、友元函数以及重载<<操作符。习题部分则为各种运算符重载以及输入输出重载

1.1 Operator overloading 运算符重载

1.2 member function, non-member function, friend function 成员函数 非成员函数 友元函数

  • 成员函数是定义在类内部的函数。它们可以访问类的所有成员变量和其他成员函数。成员函数可以是普通函数、静态函数或虚函数
  • 非成员函数是在类外部定义的函数。它们不能直接访问类的私有或保护成员,除非通过公有成员函数或友元机制。非成员函数通常用于操作多个类对象或实现某些独立于类的功能
  • 友元函数是被类声明为友元的非成员函数。友元函数可以访问类的私有和保护成员。友元函数通常用于需要直接访问类的内部数据但又不适合作为成员函数的情况 
  • 对=进行重构必须是成员函数

友元函数的优势:

相对于成员函数:

  • 灵活性:友元函数可以定义在类的外部,这使得它们在处理多个类对象时更加灵活。例如,运算符重载函数可以作为友元函数定义,以便处理不同类型的操作数。
  • 类型转换:友元函数可以对左操作数进行类型转换,这在成员函数中是不可能的。对于需要对左操作数进行类型转换的运算符重载,友元函数是一个理想的选择。
  • 减少类的耦合:友元函数可以减少类之间的耦合度。通过友元函数,一个类可以允许另一个类或函数访问其私有成员,而不需要将这些成员公开。这有助于保持类的封装性,同时提供必要的访问权限。

相较于其他非成员函数:

  • 访问私有和保护成员:友元函数可以访问类的私有和保护成员,而不需要通过公共接口。这使得友元函数在需要直接操作类的内部数据时非常有用。

总结:可以理解为友元函数占有了二者的优势。同时,友元函数是非成员函数。

  • 补充:友元类

        除了友元函数,C++还支持友元类。友元类的所有成员函数都可以访问另一个类的私有和保护成员。

  1. 定义:友元类是被另一个类声明为友元的类。友元类的所有成员函数都可以访问该类的私有和保护成员。
  2. 作用范围:友元类的所有成员函数都可以访问它被声明为友元的那个类的私有和保护成员。
  3. 使用场景:友元类通常用于两个类需要紧密协作的情况。例如,一个类需要频繁访问另一个类的内部数据。

对比友元函数和友元类:

  • 友元函数:适用于单个函数需要访问类的私有成员的情况。
  • 友元类:适用于一个类的多个成员函数需要访问另一个类的私有成员的情况。

一个例子:

1.3 Member-wise initialization 初始化

        在使用现有对象对新的对象进行初始化过程中,如果没有写相关的构造函数以提供复制功能,编译器将会自动提供一个默认的以实现复制初始化。

 1.4 Objects assignment 赋值

        在给对象赋值过程中不会调用复制的构造函数。必须对“=”运算符进行重构以实现赋值功能。 

二、习题笔记

  • 成员函数:赋值运算符 (=) 必须定义为成员函数。
  • 友元函数:输入 (>>) 和输出 (<<) 运算符必须定义为友元函数。
  • 其他运算符(如加法、减法、乘法、等于和不等于)可以根据具体需求选择定义为友元函数或成员函数。如果都是用类的对象进行运算,则可以全部定义为成员函数,如果存在int/char/double和类混合运算情况,则定义为友元函数较好。

main.cpp

#include "complex.hpp"int main() {Complex c1(3.0, 4.0), c2(1.0, 2.0), c3;bool c;cout << "c1: " << c1 << endl;cout << "c2: " << c2 << endl;c = c1 == c2;cout << "c1 和 c2 相等? " << c << endl;c = c1 != c2;cout << "c1 和 c2 不相等? " << c << endl;c3 = c1 + c2;cout << "c1 + c2: " << c3 << endl;c3 = c1 - c2;cout << "c1 - c2: " << c3 << endl;c3 = c1 * c2;cout << "c1 * c2: " << c3 << endl;cout << "输入一个复数 (c3):" << endl;cin >> c3;cout << "你输入的是: " << c3 << endl;return 0;
}

complex.hpp

#pragma once#include <iostream>
using namespace std;class Complex
{private:double realPart;double imaginaryPart;public:Complex() : realPart(0.0), imaginaryPart(0.0) {}Complex(double real, double imaginary) : realPart(real), imaginaryPart(imaginary) {}Complex& operator=(const Complex &other) {if (this != &other) {realPart = other.realPart;imaginaryPart = other.imaginaryPart;}return *this;}// the declarations, the definitions are out of the class// 显示复数friend Complex operator+(const Complex &lhs, const Complex &rhs);friend Complex operator-(const Complex &lhs, const Complex &rhs);friend Complex operator*(const Complex &lhs, const Complex &rhs);friend bool operator==(const Complex &lhs, const Complex &rhs);friend bool operator!=(const Complex &lhs, const Complex &rhs);friend std::ostream& operator<<(std::ostream &out, const Complex &c);friend std::istream& operator>>(std::istream &in, Complex &c);};

complex.cpp

#include "complex.hpp"Complex operator+(const Complex &lhs, const Complex &rhs)
{return Complex(lhs.realPart + rhs.realPart, lhs.imaginaryPart + rhs.imaginaryPart);
}Complex operator-(const Complex &lhs, const Complex &rhs)
{return Complex(lhs.realPart - rhs.realPart, lhs.imaginaryPart - rhs.imaginaryPart);
}Complex operator*(const Complex &lhs, const Complex &rhs) 
{return Complex(lhs.realPart * rhs.realPart - lhs.imaginaryPart * rhs.imaginaryPart, lhs.realPart * rhs.imaginaryPart + lhs.imaginaryPart * rhs.realPart);
}bool operator==(const Complex &lhs, const Complex &rhs) 
{return (lhs.realPart == rhs.realPart && lhs.imaginaryPart == rhs.imaginaryPart);
}bool operator!=(const Complex &lhs, const Complex &rhs) 
{return !(lhs == rhs);
}std::ostream& operator<<(std::ostream &out, const Complex &c) 
{if(c.imaginaryPart >= 0)    out << c.realPart << " + " << c.imaginaryPart << "i";elseout << c.realPart << " - " << -c.imaginaryPart << "i";return out;
}std::istream& operator>>(std::istream &in, Complex &c) 
{std::cout << "输入实部: ";in >> c.realPart;std::cout << "输入虚部: ";in >> c.imaginaryPart;return in;
}

运行结果:

编程细节tips:

  • 友元函数的声明通常放在类的任何访问控制区域(public、protected 或 private)都可以。这是因为友元函数的访问权限与它们在类中的声明位置无关。
  • 赋值运算符 operator= 通常返回一个对当前对象的引用(Complex&),这是为了支持链式赋值操作,并确保赋值操作的效率和正确性。

        1. 支持链式赋值:返回当前对象的引用允许链式赋值操作。例如:

Complex c1, c2, c3; 
c1 = c2 = c3; 

        在这个例子中,c2 = c3 返回 c2 的引用,然后 c1 = c2 使用这个引用进行赋值。如果赋值运算符不返回引用,链式赋值将无法正常工作。

        2. 提高效率:返回引用避免了不必要的对象拷贝。赋值运算符直接返回当前对象的引用,而不是返回一个新的对象,从而提高了效率。

        3. 符合C++标准惯例:返回当前对象的引用是C++标准库中赋值运算符的惯例。这种做法确保了代码的一致性和可读性。

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

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

相关文章

nginx 版本升级

Nginx 的版本最开始使用的是 Nginx-1.18.0 &#xff0c; 由于服务升级&#xff0c;需要将 Nginx 的版本升级到 Nginx-1.19.7 &#xff0c;要求 Nginx 不能中断提供服务。 为了应对上述的需求&#xff0c;提供两种解决方案&#xff1a; 方案1&#xff1a; make upgrade 完成升…

包装类和泛型

&#x1f389;欢迎大家收看&#xff0c;请多多支持&#x1f339; &#x1f970;关注小哇&#xff0c;和我一起成长&#x1f680;个人主页&#x1f680; 包装类&#x1f319; Java中每个基本数据类型都对应了一个包装类&#xff0c; 除了int的包装类是Integer&#xff0c;char…

STM32项目分享:智能风扇系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com/video/BV1xw4m1Y7sA…

详解 @RequestHeader 注解在 Spring Boot 中的使用

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

springboot鲜花商城平台-计算机毕业设计源码56085

基于微信小程序的鲜花商城平台设计与实现 摘 要 鲜花商城小程序的研究旨在设计和开发一个方便、快捷的移动应用平台&#xff0c;为用户提供鲜花购买、资讯浏览和社交互动等功能。该研究包括以下几个方面的内容&#xff1a;首先&#xff0c;通过调研和分析鲜花市场和用户需求&a…

【你也能从零基础学会网站开发】 SQL结构化查询语言应用基础--DDL篇--SQL Server数据库开发之ALTER TABLE修改表语句使用详解

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 ALTER TABLE 语…

Docker NameSpace隔离

1、dd命令&#xff1a;dd 可从标准输入或文件中读取数据&#xff0c;根据指定的格式来转换数据&#xff0c;再输出到文件、设 备或标准输出 功能&#xff1a;用于读取、转换并输出数据 语法&#xff1a;dd OPTION 参数 1、 if文件名&#xff1a;输入文件名&#xff0c;默认为…

【C++】C++的类型的转换

目录 C语言中的类型转换 C中的类型转换 C强制类型转换 static_cast reinterpret_cast const_cast dynamic_cast C语言中的类型转换 C语言中又两种类型转换&#xff1a;&#xff08;强制&#xff09;显示类型转换和隐式类型转换。 &#xff08;强制&#xff09;显示类型…

Linux系统服务——【web,http协议,apache服务和nginx服务】(sixteen day)

一、web基础以及http协议 1、web基本概念和常识 前端开发一般用uniapp. 1、Web:为用户提供的一种在互联网上浏览信息的服务&#xff0c;Web 服务是动态的、可交互的、跨平台的和图形化的。 2、Web 服务为用户提供各种互联网服务&#xff0c;这些服务包括信息浏览服务&#xf…

深入源码:解析SpotBugs (3) Detector

文章目录 OpcodeStackDetector常用套路调用栈visit code类检测方法检测代码行检测 前面的博客也提到过&#xff0c;Spotbugs 里面 Detector2 与 Detector&#xff0c;FindBugs2 与 FindBugs&#xff0c;GUI2与GUI&#xff0c;可以视为 Spotbugs 与 FindBugs 新老技术的碰撞&…

STM32单片机C语言:继电器控制220v灯泡亮灭

本文旨在详细阐述如何利用STM32单片机结合继电器模块&#xff0c;实现对220V灯泡亮灭的远程控制。我们将深入探讨继电器的工作原理&#xff0c;构建相应的硬件电路&#xff0c;并提供具体的程序实现步骤&#xff0c;在智能家居与自动化控制领域的应用的比较多。 一、继电器原理…

接口测试支持IDEA插件一键同步API、新增思维导图快速评审测试用例,MeterSphere开源持续测试工具v3.1.0版本发布

2024年7月29日&#xff0c;MeterSphere开源持续测试工具正式发布v3.1.0版本。 在这一版本中&#xff0c;接口测试方面&#xff0c;支持通过IDEA插件一键同步API至MeterSphere&#xff1b;测试管理方面&#xff0c;“测试用例”模块新增通过思维导图模式快捷评审测试用例。在“…

扫码登录方案

以哔哩哔哩扫码登录为例 二维码解码后内容为&#xff1a;https://passport.bilibili.com/h5-app/passport/login/scan?navhide1&qrcode_keye60869ce7f5235c7123175a7effc6f90&frommain-fe-header 扫码登陆&#xff0c;利用已登录设备授权未登录设备登录的方式 扫码…

使用JavaFx Fxml笔记

使用JavaFx Fxml实现账号密码登录 HelloApplication.java&#xff1a;package com.example.dr295cmonth7;import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.scene.Parent; import javafx.scene.Scene; i…

【论文精读】 | 基于图表示的视频抑郁症识别的两阶段时间建模框架

文章目录 0、Description1、Introduction2、Related work2.1 Relationship between depression and facial behaviours2.2 Video-based automatic depression analysis2.3 Facial graph representation 3、The proposed two-stage approach3.1 Short-term depressive behaviour…

请你谈谈:vue的渲染机制(render)- 2举例说明问题

如何在 Vue 的 render 函数中使用 createElement 方法来创建虚拟节点&#xff08;VNode&#xff09;。这里是一个稍微整理后的示例&#xff0c;它直接对应于你提供的注释和代码片段&#xff0c;但作为一个完整的 render 函数的一部分&#xff0c;可能位于一个 Vue 组件的 scrip…

javascript(一)

一、基本语法 1.位置 (1)JavaScript脚本必须位于<script>与</script>之间 (2)<script>标签可以位于<body>或者<head>部分中 2.输出语句 (1)window.alter() 弹出警告框 (2)document.write() 可以将内容在网页中打印出来&#xff0c;同时也…

二维01背包 背包滚动数组 分割等和子集 DAY22

11.背包理论基础 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 背包问题有多种背包方式&#xff0c;常见的有&#xff1a;01背包、完全…

进程间通信方式--管道

每个进程的用户地址空间都是独立的&#xff0c;一般而言是不能互相访问的&#xff0c;但内核空间是每个进程都共享的&#xff0c;所以进程之间要通信必须通过内核。 管道 管道的linux命令&#xff1a;ps auxf | grep mysql 上面命令行里面的竖线就是一个管道&#xff0c;它的功…

新手vue学习问题汇总(自用)(长期更新)

1.export default export default 是 ES6 模块语法&#xff0c;用于导出模块的默认成员。在 Vue.js 中&#xff0c;通常用来导出一个组件对象&#xff0c;使其可以在其他文件中被导入并使用。 2.props props 是组件接收外部数据的方式。父组件可以通过向子组件传递 props 来…