C++ 运算符重载详解

本篇内容来源于对c++课堂上学习内容的记录

通过定义函数实现任意数据类型的运算

假设我们定义了一个复数类,想要实现两个复数的相加肯定不能直接使用“+”运算符,我们可以通过自定义一个函数来实现这个功能:

#include <iostream>
using namespace std;
class Complex                                     //定义Complex类
{public:
Complex( ){real=0;imag=0;}                      //定义构造函数
Complex(double r,double i){real=r;imag=i;}     //构造函数重载
Complex complex_add(Complex &c2);              //声明复数相加函数
void display( );                                //声明输出函数private:
double real;                                   //实部
double imag;                                   //虚部
};
Complex Complex∷complex_add(Complex &c2)
{	Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;return c;
}   
void Complex∷display( )                            //定义输出函数
{cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}
int main( )
{
Complex c1(3,4),c2(5,-10),c3;                     //定义3个复数对象
c3=c1.complex_add(c2);                            //调用复数相加函数
cout<<″c1=″; c1.display( );                        //输出c1的值
cout<<″c2=″; c2.display( );                        //输出c2的值
cout<<″c1+c2=″; c3.display( );                     //输出c3的值
return 0;
}

使用运算符重载实现上述功能

运算符重载是指在编程语言中,为用户自定义的数据类型定义新的行为,使其可以像内置数据类型一样使用运算符。通过运算符重载,程序员可以对用户自定义类型进行特定的操作,而不仅仅局限于语言提供的默认行为。

在大多数编程语言中,运算符重载通常涉及到重定义类或对象的特定方法,以实现对应运算符的功能。具体步骤包括:

选择运算符: 选择要重载的运算符。这可以是算术运算符(如+-)、关系运算符(如==!=)、位运算符、赋值运算符等。

定义重载函数: 在类或对象中定义与选择的运算符相关的特定方法。这些方法通常被称为运算符重载函数。在许多语言中,这些函数具有特殊的名称,例如C++中的 operator+operator==

对于上述的例子,要想使用运算符重载,仅仅需要把复数类中的函数名称由complex_add变成operator+即可,然后就可以直接使用运算符“+”对两个复数进行相加:

重载运算符的一些规则

1.C++不允许用户自己定义新的运算符,只能对已有的运算符进行重载

2.C++有几个运算符是不能重载的,分别是:

.(成员访问运算符)

*(成员指针访问运算符)

::(域运算符)

sizeof(长度运算符)

?:(三目运算符)

3.重载不能改变操作数个数

4.重载不能改变优先级

5.重载不能改变结合性

6.重载的运算符必须和用户定义类型一起使用,参数不能全是C++标准类型

7.有些运算符不用重载就可以对用户定义类型的对象使用,比如“=”和“&”

友元重载运算符

首先简单解释一下友元

在面向对象编程中,友元(Friend)是一种机制,允许一个类或函数访问另一个类的私有成员。这就意味着,如果一个类或函数被声明为另一个类的友元,它就可以直接访问该类的私有成员,而不受访问权限的限制。

友元的主要用途是在某些情况下,允许外部的类或函数访问另一个类的私有部分,以实现更灵活的设计或提高效率。一般而言,友元关系是单向的,即如果类 A 是类 B 的友元,不一定意味着类 B 是类 A 的友元

友元在重载运算符中有什么用呢?

考虑这么一个问题,我想让上述的复数和一个整数相加怎么办,其实很简单,只需要把operator+中的参数类型由Complex改成int就行了,这样,在下面调用的时候,就可以写:

Complex c=a+2;

但是,如果我们写成:

Complex c=2+a;有没有问题呢?当然有问题,这样就是参数列表中的类型顺序不照应了

因此,如果运算符左侧操作数属于C++标准类型或者是一个其它类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数,如果函数需要访问类的私有成员,则必须声明为友元函数

举个例子:

#include <iostream>
using namespace std;
class Complex
{public:
Complex( ){real=0;imag=0;}
Complex(double r,double i)
{real=r;imag=i;}
friend Complex operator +(Complex &c1,Complex &c2);
//重载函数作为友元函数
void display( );private:
double real;
double imag;
};
Complex operator + (Complex &c1,Complex &c2)         //定义作为友元函数的重载函数
{return Complex(c1.real+c2.real, c1.imag+c2.imag);}
void Complex∷display( )
{cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}int main( )
{Complex c1(3,4),c2(5,-10),c3;
c3=c1+c2;
cout<<″c1=″; c1.display( );
cout<<″c2=″; c2.display( );
cout<<″c1+c2 =″; c3.display( );}

单目运算符的重载

由于单目运算符本身就一个参数,那么如果运算符重载函数作为成员函数的话,就可以忽略参数

首先看自增运算符++和自减运算符--的重载

此时我们面临一个问题,这两个符号分为前置和后置两种情况,如果区分呢?

C++约定: 在自增(自减)运算符重载函数中,增加一个int型形参,就是后置自增(自减)运算符函数

重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用

举个例子:

#include <iostream>class Counter {
private:int count;public:Counter() : count(0) {}// 重载前置自增运算符 (++var)Counter& operator++() {count++;return *this;  // 返回递增后的对象引用}// 重载后置自增运算符 (var++)Counter operator++(int) {Counter temp(*this);  // 创建一个副本用于保存递增前的值count++;return temp;  // 返回递增前的对象副本}void display() const {std::cout << "Count: " << count << std::endl;}
};int main() {Counter myCounter;// 使用前置自增运算符++myCounter;myCounter.display();// 使用后置自增运算符Counter anotherCounter = myCounter++;myCounter.display();anotherCounter.display();return 0;
}

重载“<<”和">>"

格式:

重载"<<"

#include <iostream>class Point {
private:int x, y;public:Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}// 重载输出运算符friend std::ostream& operator<<(std::ostream& out, const Point& point);
std::ostream& operator<<(std::ostream& out, const Point& point) {out << "Point(" << point.x << ", " << point.y << ")";return out;
}
int main() {Point myPoint(3, 4);// 使用重载的输出运算符std::cout << "My Point: " << myPoint << std::endl;return 0;
}

为什么在函数当中最后要返回out呢?

可以满足链式编程,实现类似:cout<<myPoint1<<myPoint2<<endl;的输出

下面看对">>"的重载

#include <iostream>class Point {
private:int x, y;public:Point() : x(0), y(0) {}// 重载输入运算符friend std::istream& operator>>(std::istream& in, Point& point);// 用于显示坐标的成员函数void display() const {std::cout << "Point(" << x << ", " << y << ")";}
};
std::istream& operator>>(std::istream& in, Point& point) {std::cout << "Enter x-coordinate: ";in >> point.x;std::cout << "Enter y-coordinate: ";in >> point.y;return in;
}int main() {Point myPoint;// 使用重载的输入运算符std::cout << "Please enter coordinates for a point:\n";std::cin >> myPoint;// 使用成员函数显示坐标std::cout << "You entered: ";myPoint.display();std::cout << std::endl;return 0;
}

转换构造函数和类型转换函数

转换构造函数(conversion constructor function) 的作用是将一个其他类型的数据转换成一个类的对象,本质是就是一种特殊的有参构造函数

class Celsius {
private:double temperature;public:// 转换构造函数,将double类型转换为Celsius对象Celsius(double temp) : temperature(temp) {}void display() {std::cout << "Temperature in Celsius: " << temperature << " C" << std::endl;}
};int main() {// 使用转换构造函数,将double类型转换为Celsius对象Celsius celsiusObject = 25.5;celsiusObject.display();return 0;
}

类型转换函数:用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。 C++提供类型转换函数(type conversion function)来解决这个问题

一般形式

operator 类型名()

{实现转换的语句}

注意:

1.类型转换函数只能作为成员函数,因为转换的主体是本类对象,不能作为友元函数或普通函数

2.从函数形式可以看到,它与运算符重载函数相似,都是用关键字operator开头,只是被重载的是类型名

3.程序中的Complex类对象是不是一律都转换成为double类型数据?否,Complex对象既是Complex对象,也可以作为double类型数据,需要时才进行转换

针对最前面那个复数类的例子,如果在类中定义:

operator double(){return real;}

在main函数中如果有:

Complex a(1,2);

double d=a+2.5;

此时在没有特定运算符重载函数的情况下,编译器将会自动把a转换为double和2.5相加

假如程序中需要对一个Complex类对象和一个double型变量进行+,-,*,/等算术运算,以及关系运算和逻辑运算,如果不用类型转换函数,就要对多种运算符进行重载,以便能进行各种运算 如果用类型转换函数对double进行重载(使Complex类对象转换为double型数据),就不必对各种运算符进行重载,因为Complex类对象可以被自动地转换为double型数据,而标准类型的数据的运算,是可以使用系统提供的各种运算符的

注意!如果同时有转换构造函数和类型转换函数,可能出现二义性!

比如进行上述的double d=a+2.5;时,我到底是把a通过类型转换函数变成double呢,还是让2.5通过转换构造函数变为Complex呢???在使用的时候要注意这一点

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

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

相关文章

宠物信息服务预约小程序的效果如何

宠物的作用越来越重要&#xff0c;因此铲屎官们对自己爱宠的照顾也是加倍提升&#xff0c;而市场围绕宠物展开的细分服务近些年来逐渐增多&#xff0c;且市场规模快速增长。涉及之广&#xff0c;涵盖宠物衣食住行、医疗、美容、婚丧嫁娶等&#xff0c;各品牌争相抢夺客户及抢占…

代码随想录算法训练营|五十六天

回文子串 647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; dp含义&#xff1a;表示区间内[i,j]是否有回文子串&#xff0c;有true&#xff0c;没有false。 递推公式&#xff1a;当s[i]和s[j]不相等&#xff0c;false&#xff1b;相等时&#xff0c;情况一&#xff0c;…

中国电影票房排行数据爬取及分析可视化

大家好&#xff0c;我是带我去滑雪&#xff01; 对中国电影票房排行数据的爬取和分析可视化具有多方面的用处&#xff1a;例如了解电影市场的历史趋势&#xff0c;包括不同类型电影的受欢迎程度、票房的季节性波动。识别观众对于不同类型电影的偏好&#xff0c;为电影制片方提供…

高效背单词——单词APP安利

大英赛&#xff0c;CET四六级&#xff0c;以及考研英语&#xff0c;都在不远的未来再度来临&#xff0c;年复一年的考试不曾停息&#xff0c;想要取得好成绩&#xff0c;需要我们的重视并赋予相应的努力。对于应试英语&#xff0c;词汇量是不可忽略的硬性要求。相比于传统默写&…

快速集成Skywalking 9(Windows系统、JavaAgent、Logback)

目录 一、Skywalking简介二、下载Skywalking服务端三、安装Skywalking服务端3.1 解压安装包3.2 启动Skywalking 四、关于Skywalking服务端更多配置五、Java应用集成skywalking-agent.jar5.1 下载SkyWalking Java Agent5.2 集成JavaAgent5.3 Logback集成Skywalking5.4 集成效果 …

flutter web 中嵌入一个html

介绍 flutter web 支持使用 HtmlElementView嵌入html import dart:html; import dart:ui as ui; import package:flutter/cupertino.dart;class WebWidget extends StatelessWidget {const WebWidget({super.key});overrideWidget build(BuildContext context) {DivElement fr…

【diffuser系列】ControlNet

ControlNet: TL;DRControl TypeStableDiffusionControlNetPipeline1. Canny ControlNet1.1 模型与数据加载1.2 模型推理1.3 DreamBooth微调 2. Pose ControlNet2.1 数据和模型加载2.2 模型推理 ControlNet: TL;DR ControlNet 是在 Lvmin Zhang 和 Maneesh Agrawala 的 Adding …

麦克风阵列入门

文章引注&#xff1a; http://t.csdnimg.cn/QP7uC 一、麦克风阵列的定义 所谓麦克风阵列其实就是一个声音采集的系统&#xff0c;该系统使用多个麦克风采集来自于不同空间方向的声音。麦克风按照指定要求排列后&#xff0c;加上相应的算法&#xff08;排列算法&#xff09;就可…

漫谈广告机制设计 | 万剑归宗:聊聊广告机制设计与收入提升的秘密(2)

书接上文漫谈广告机制设计 | 万剑归宗&#xff1a;聊聊广告机制设计与收入提升的秘密&#xff08;1&#xff09;&#xff0c;我们谈到流量作为一种有限资源&#xff0c;其分配方式&#xff08;或者交易方式&#xff09;也经历了几个阶段&#xff1a;第一个是谈判定价阶段&#…

线性方程组

线性方程组 设存在线性方程组 { a 1 , 1 x 1 a 1 , 2 x 2 ⋯ a 1 , n x n b 1 a 2 , 1 x 1 a 2 , 2 x 2 ⋯ a 2 , n x n b 2 ⋮ ⋮ a m , 1 x 1 a m , 2 x 2 ⋯ a m , n x n b m \left.\left\{\begin{array}{l}a_{1,1}x_1a_{1,2}x_2\cdotsa_{1,n}x_nb_1\\a_{2,1}…

linux查看资源占用情况常用命令

1. 查看 CPU 使用情况&#xff1a; top这个命令会显示系统中当前活动进程的实时信息&#xff0c;包括 CPU 使用率、内存使用率等。按 q 键退出。 2. 查看内存使用情况&#xff1a; free -m这个命令显示系统内存的使用情况&#xff0c;以兆字节&#xff08;MB&#xff09;为…

【预处理详解】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 预定义符号 2. #define定义常量 3. #define定义宏 4. 带有副作用的宏参数 5. 宏替换的规则 6. 宏函数的对比 7. #和## 7.1 #运算符 7.2 ## 运算符 8. 命名约定 …

【Spring总结】注解开发

本篇讲的内容主要是基于Spring v2.5的注解来完成bean的定义 之前都是使用纯配置的方式来定义的bean 文章目录 前言1. Spring v2.5 注解开发定义bean第一步&#xff1a;在需要定义的类上写上注解Component第二步&#xff1a;在Spring Config中定义扫描包第三步&#xff1a;主方法…

企业数字化过程中数据仓库与商业智能的目标

当前环境下&#xff0c;各领域企业通过数字化相关的一切技术&#xff0c;以数据为基础、以用户为核心&#xff0c;创建一种新的&#xff0c;或对现有商业模式进行重塑就是数字化转型。这种数字化转型给企业带来的效果就像是一次重构&#xff0c;会对企业的业务流程、思维文化、…

为什么求职者反感企业招聘用的人才测评?

为什么求职者会对人才测评的不满&#xff1f;大概率是认为性格测评不能完整的定义人的优势&#xff0c;也就是测不准&#xff01; 这个想法是对的&#xff0c;性格测评并不能100%的展现一个完整的人&#xff0c;目前没有那个测评的信效度能达到如此理想&#xff0c;估计以后也…

Elasticsearch中的语义检索

一、传统检索的背景痛点 和传统的基于关键词的匹配方式不同&#xff0c;语义检索&#xff0c;利用大模型&#xff0c;将文本内容映射到神经网络空间&#xff0c;最终记忆token做检索。 例如想要搜索中国首都&#xff0c;例如数据集中&#xff0c;只有一篇文章在描述北京&#x…

Pandas分组聚合_Python数据分析与可视化

Pandas分组聚合 分组单列和多列分组Series 系列分组通过数据类型或者字典分组获取单个分组对分组进行迭代 聚合应用单个聚合函数应用多个聚合函数自定义函数传入 agg() 中对不同的列使用不同的聚合函数 分组聚合的流程主要有三步&#xff1a; 分割步骤将 DataFrame 按照指定的…

Leetcode经典题目之“双指针交换元素“类题目

1 LC 27. 移除元素 class Solution {public int removeElement(int[] nums, int val) {int nnums.length;int s0;for(int i0;i<n;i){// 只有不等于目标值的时候才会进行交换&#xff0c;然后移动s指针if(nums[i]!val){swap(nums,i,s);}}return s;}void swap(int[]nums, int…

【cpolar】Ubuntu本地快速搭建web小游戏网站,公网用户远程访问

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;cpolar&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言 1. 本地环境服务搭建 2. 局域网测试访问 3. 内网穿透 3.1 ubuntu本地安装cpolar 3.2 创建隧道 3.3 测试公网访问 4. 配置…