C++核心编程——运算符重载

C++核心编程——运算符重载

  • 运算符重载的方法
  • 运算符重载函数作成员函数与友元函数
  • 重载双目运算符
  • 重载单目运算符
  • 重载流插入运算符和"<<"和流提取运算符">>"
    • 重载流插入运算符和"<<"
    • 流提取运算符">>"

运算符重载的方法

运算符重载的方法是定义一个重载运算符的函数,使指定运算符不能能实现原有的功能,还能实现在函数中指定新的功能。运算符重载的实质是函数的重载。
重载运算符的函数一般格式如下:
函数类型 operator 运算符名称(形参表)
{ 对运算符的重载处理 }

在这里插入图片描述
典例:对运算符 “+” 实现重载,使之能用于两个复数相加。

#include <iostream>
using namespace std;
class Complex{
public:Complex(){real=0;imag=0;}//自定义有参构造 无参构造不再提供,若要用,需要自定义 Complex(int, int);Complex operator+(Complex &c);void display();private:int real;int imag;
};Complex::Complex(int real, int imag)
{this->real = real;this->imag = imag;
}Complex Complex::operator+(Complex &c)
{Complex tempc;tempc.real = real + c.real;tempc.imag = imag + c.imag;return tempc;
}
void Complex::display()
{cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{Complex c1(3,4);Complex c2(5,-10);Complex c3;c3 = c1 + c2; 	//相当于:c3 = c1.operator+(c2); cout << "c1=";		c1.display();cout << "c2=";		c2.display();cout << "c1+c2=";	c3.display();return 0;
} 

说明:

  • 自定义有参构造函数,则无参构造不再提供,若要用,需要重新自定义
  • 程序 c3 = c1 + c2 相当于 c3 = c1.operator+(c2); ,使用运算符重载能使用户程序易于编写,阅读和维护。

运算符重载函数作成员函数与友元函数

在这里插入图片描述
如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。必须要求运算表达式(如c1+c2)中第1个参数即运算符左侧的操作数)是一个类对象而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。

  • 如果想要计算复数+整数,如c1+i,需要重新重载运算符
    Complex Complex::operator+(int i)
    {Complex tempc;tempc.real = real + i;tempc.imag = imag;return tempc;
    }
    
  • 如果想要计算整数+复数,如i+c1, 则第一个参数不是类对象,必须使用友元函数重载运算符。
    Complex operator+(int i, Complex &c)
    {Complex tempc;tempc.real = i + c.real;tempc.imag = c.imag;return tempc;
    }
    
  • 一般将双目运算符重载为友元函数
    Complex operator+(Complex &c1, Complex &c2)
    {Complex tempc;tempc.real = c1.real + c2.real;tempc.imag = c1.imag + c2.imag;return tempc;
    }
    

从原则上讲,要尽量将重载运算符函数作为成员函数,但还因考虑其他各方因素和习惯,以下方式可供参考
(1)C++规定,赋值运算符“=”、下标运算符“[]”、函数调用运算符“()”、成员运算符“->”、必须作为成员函数重载。
(2)流插人“<<”和流提取运算符“>>”类型转换运算符函数不能定义为类的成员函数,只能作为友元函数。
(3)一般将单目运算符和复合运算符( +=,-=,/=,*=,&=,!=,^=,%=,>>=,<<=)重载为成员函数。
(4)一般将双目运算符重载为友元函数

完整代码(可供参考)

#include <iostream>
using namespace std;
class Complex{
public:Complex(){real=0;imag=0;}Complex(int, int);Complex::operator+(int i);friend operator+(int i, Complex &c);friend Complex operator+(Complex &c1, Complex &c2);void display();private:int real;int imag;
};//构造函数 
Complex::Complex(int real, int imag)
{this->real = real;this->imag = imag;
}//复数+整数 
Complex Complex::operator+(int i)
{Complex tempc;tempc.real = real + i;tempc.imag = imag;return tempc;
}//整数 +复数 
Complex operator+(int i, Complex &c)
{Complex tempc;tempc.real = i + c.real;tempc.imag = c.imag;return tempc;
}
//复数+复数 一般情况下双目运算符重载为友元函数 
Complex operator+(Complex &c1, Complex &c2)
{Complex tempc;tempc.real = c1.real + c2.real;tempc.imag = c1.imag + c2.imag;return tempc;
}void Complex::display()
{cout << "(" << real << "," << imag << "i)" << endl;
}int main()
{Complex c1(3,4);Complex c2(5,-10);Complex c3,c4,c5;c3 = c1 + c2; 	//相当于:c3 = c1.operator+(c2); c4 = c1 + 4;c5 = 5 + c2;cout << "c1=";		c1.display();cout << "c2=";		c2.display();cout << "c1+c2=";	c3.display();cout << "c1+4=";	c4.display();cout << "5+c2=";	c5.display();return 0;
} 

重载双目运算符

双目运算符是C++中最常见的运算符,双目运算符有两个操作数,通常在运算符的左右侧,重载函数应该有两个参数,也通常设置为友元函数作为运算符重载函数。
典例:声明一个字符串类String,用来存放不定长的字符串,重载运算符“==”和“>”,用于两个字符串的等于、小于和大于的比较运算。
为了程序的设计了与便于理解,程序设计分步骤编写。
1. 先建立一个String类

#include <iostream>
#include <string.h>using namespace std;
class String{
public://构造及打印函数 String(){p=NULL;}		//无参构造 String(char *str){p=str;}	//有参构造 void display(){cout << p << endl;}	//打印字符串 	
private:char *p;
};int main()
{//测试代码 String str1("Hello"),str2("world");str1.display();str2.display();return 0;
} 

2. 重载运算符>
通过全局函数作类友元的方式重载运算符>,其重载函数实现体如下所示。

bool operator>(String &str1, String &str2)
{if(strcmp(str1.p,str2.p)>0)return true;elsereturn false; 
}

这只是一个并不很完善的程序,但是,已经完成实质性的工作了,运算符重载成功了。既然对运算符“>”的重载成功了,其他两个运算符的重载如法炮制即可
3. 重载其他运算符

  • 重载运算符“<”

    重载运算符< 
    bool operator<(String &str1, String &str2)
    {if(strcmp(str1.p,str2.p)<0)return true;elsereturn false; 
    }
    
  • 重载运算符“==”

    //重载运算符= 
    bool operator==(String &str1, String &str2)
    {if(strcmp(str1.p,str2.p)==0)return true;elsereturn false; 
    }
    
  • 同时记得将上述三个重载函数声明为类的友元函数

    //运算符重载函数 
    friend bool operator>(String &str1, String &str2);
    friend bool operator<(String &str1, String &str2);	
    friend bool operator==(String &str1, String &str2);	
    
  • 测试代码:

    int main()
    {//测试代码 String str1("Hello"),str2("world");cout << (str1>str2) << endl; cout << (str1<str2) << endl; cout << (str1==str2) << endl; return 0;
    } 
    
  • 测试结果

    0
    1
    0

4. 修饰完善,优化输出

//封装比较函数 优化输出
void compare(String &str1, String &str2)
{if((str1 > str2)==1){str1.display();	cout << " > ";	str2.display();}else if ((str1 < str2)==1){str1.display(); cout << " < ";	str2.display(); }	else{str1.display(); cout << " = ";	str2.display();}	
}

增加了一个compare函数用来对两个字符串进行比较,并输出相应的信息。这样可以减轻主函数的负担,同时使主函数简明易读。
先搭框架、逐步扩充、由简到繁、最后完善。边编程、边调试、边扩充。
完整参考程序:

#include <iostream>
#include <string.h>using namespace std;
class String{
public://构造及打印函数 String(){p=NULL;}			//无参构造 String(char *str){p=str;}	//有参构造 void display(){cout << p;}	//打印字符串//运算符重载函数 friend bool operator>(String &str1, String &str2);friend bool operator<(String &str1, String &str2);	friend bool operator==(String &str1, String &str2);	private:char *p;
};void compare(String &str1, String &str2)
{if((str1 > str2)==1){str1.display();	cout << " > ";	str2.display();}else if ((str1 < str2)==1){str1.display(); cout << " < ";	str2.display(); }	else{str1.display(); cout << " = ";	str2.display();}	
}int main()
{//测试代码 String str1("hello"),str2("world");compare(str1,str2);return 0;
} //重载运算符> 
bool operator>(String &str1, String &str2)
{if(strcmp(str1.p,str2.p)>0)return true;elsereturn false; 
}重载运算符< 
bool operator<(String &str1, String &str2)
{if(strcmp(str1.p,str2.p)<0)return true;elsereturn false; 
}//重载运算符= 
bool operator==(String &str1, String &str2)
{if(strcmp(str1.p,str2.p)==0)return true;elsereturn false; 
}

重载单目运算符

重载单目运算符与重载双目运算符相似,但由于单目运算符只有一个操作数,因此单目运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数。

典例:以自增运算符++为例,设计一个Time类,含数据成员minute(分)和sec(秒),模拟秒表,每次走1秒,满60秒进1分钟,此时秒又从0起算。要求输出分和秒的值。

#include <iostream>
#include <string.h>using namespace std;
class Time{
public://构造及打印函数 Time(){min=0;sec=0;}Time(int,int);void display();//运算符重载Time operator++();private:int min;int sec;
};//构造函数 
Time::Time(int min, int sec)
{this->min = min;this->sec = sec;
}
//运算符重载 
Time Time::operator++()
{sec++;if(sec>=60){min++;sec=0;}return *this;	
}
//打印时间	
void Time::display()
{cout << min << ":" << sec << endl;
}int main()
{int i;Time time1(34,12);for(i=0;i<61;i++){++time1;time1.display();}return 0;
} 

程序对运算符“++”进行了重载,使它能用于Time类对象。
但问题是:“++”和“–”运算符有两种使用方式,前置自增运算符和后置自增运算符,它们的作用是不一样的,在重载时怎样区别这二者呢?
针对“++”和“–”这一特点,C++约定:在自增(自减)运算符重载函数中增加一个int型形参就是后置自增(自减)运算符函数。
在上述程序的基础上增加对后置自增运算符的重载。

  • ++运算符重载后置 重载函数
    //运算符重载 ++ 后置 
    Time Time::operator++(int)
    {//建立临时对象 temp 并读取当前值 Time temp(*this);	sec++;if(sec>=60){min++;sec=0;}return temp;	
    }
    
  • 主函数程序程序
    int main()
    {int i;Time time1(34,12);Time time2;time2=time1++;cout << "time2=tim1++: time1=";	time1.display();	cout << "  time2=";	time2.display();return 0;
    } 
    

重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用。在定义函数时也不必使用此参数因此可省写参数名,只须在括号中写int 即可。

重载流插入运算符和"<<“和流提取运算符”>>"

如果想用输出和输入自已声明的类型的数据,必须对它们重载
对"<<“和”>>"重载的函数形式如下:
ostream & operator<<(ostream &, 自定义类 &)
istream & operator>>(istream &, 自定义类 &)
重载“>>”和“<<”的函数只能作为友元函数,不能将他们定义为成员函数

重载流插入运算符和"<<"

典例:在上述代码的基础上,重载输出运算符“<<”输出复数

#include <iostream>
#include <string.h>using namespace std;
class Complex{
public://构造及打印函数 Complex(){real=0;imag=0;}					//无参构造 Complex(double i,double j){real=i;imag=j;}	//有参构造 Complex operator+(Complex &);				//+号运算符重载 friend ostream &operator<<(ostream &output, Complex &c);	//重载coutprivate:double real;double imag;
};Complex Complex::operator+(Complex &c)
{Complex temp;temp.real = real + c.real;temp.imag = imag + c.imag;return temp;
} ostream &operator<<(ostream &output, Complex &c)
{output << "(" << c.real << "," << c.imag << "i)";return output;
}int main()
{//测试代码 Complex c1(1.2, 2.3);Complex c2(0.8, 2.7);Complex c3;c3 = c1 + c2;cout << c1 << "+" << c2 << "=" << c3;return 0;
}

程序中重载了运算符“<<”运算符重载函数“operator<<”中的形参output是ostream类对象的引用,形参名output是用户任意起的。
  运算符“<<”的左面是cout,前面已提到cout是在头文件iostream中声明的ostream类对象。“<<”的右面是c3,它是Complex类对象。由于已将运算符“<<”的重载函数声明为Complex类的友元函数编译系统把cout<<c3解释为:
  operator<<(cout, c1)
即以cout和c1作为实参调用下面的“operator<<”函数:

ostream &operator<<(ostream &output, Complex &c)
{output << "(" << c.real << "+" << c.imag << "i)";return output;
}

调用函数时,形参output成为实参cout的引用,形参c成为c1的引用。因此调用函数的过程相当于执行

cout<< "(" << c1.real << "," << c1.imag << "i)";

return output的作用:
  连续向输出流插入信息,output是ostream类的对象的引用,因此return output就是return cout.

流提取运算符">>"

  • >>运算符重载函数
    istream &operator>>(istream &input, Complex &c) 
    {input >> c.real >> c.imag;return input;
    }
    
    函数的返回值仍然为输入流的引用,第一个参数为输入流的引用,第二个参数为复数的引用,当调用以下函数时,第一个参数被赋值为cin的引用,第二个参数被赋值为c1的引用,程序就相当于执行cin >> c1.real >> c1.imag,执行完毕后返回istream的引用,使得能够继续连续输入c2.
  • 函数的调用
    cin >> c1 >> c2;	//其中c1,c2代表2个复数
    

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

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

相关文章

LabVIEW开发自适应降噪ANC

LabVIEW开发自适应降噪ANC 在许多情况下&#xff0c;信号很嘈杂&#xff0c;必须消除噪声。自适应降噪&#xff08;ANC&#xff09;是可用于消除信号噪声的主要实时方法之一。可以使用LabVIEW自适应滤滤器工具包来设计ANC应用程序。本文介绍使用自适应筛选器工具包的ANC的一些…

【深度学习】概率图模型(二)有向图模型详解(条件独立性、局部马尔可夫性及其证明)

文章目录 一、有向图模型1. 贝叶斯网络的定义2. 条件独立性及其证明a. 间接因果关系 X 3 → X 2 → X 1 X_3 \rightarrow X_2 \rightarrow X_1 X3​→X2​→X1​b. 间接果因关系 X 1 → X 2 → X 3 X_1 \rightarrow X_2 \rightarrow X_3 X1​→X2​→X3​c. 共因关系 X 1 ← X…

java中如何将mysql里面的数据取出来然后通过stream流的方式进行数据处理代码实例?

在 Java 中使用 Stream 流的方式从 MySQL 数据库中取出数据并进行处理&#xff0c;你可以通过 JDBC&#xff08;Java Database Connectivity&#xff09;来实现。下面是一个简单的代码示例&#xff1a; import java.sql.*; import java.util.stream.Stream; public class MySQ…

android上架之获取平台公钥、签名 MD5 值

app上架需要用到的两个参数公钥、签名 MD5 步骤 1. 下载jadx-gui 工具&#xff0c;下载 2. 下载成后&#xff0c;解压压缩包&#xff0c;双击 jadx-gui-1.4.7.exe 运行。 3. 运行后&#xff0c;在页面左上方单击文件 > 打开文件&#xff0c;打开 APK 包 4. 打开 APK 包后…

Jvm常见问题

1. 为什么用元空间替换永久代 避免OOM异常&#xff1a;永久代中存放了很多JVM需要的类信息&#xff0c;这些数据大多数是不会被清理的&#xff0c;所以Full GC往往无法回收多少空间。而永久代的空间是有限的&#xff0c;如果经常加载新的类进来或者频繁的创建和删除类&#xf…

前端页面转pdf

首先&#xff0c;需要安装两个库 html2canvasjspdf 先引入这个公用的html转pdf的方法 /**path:src/utils/htmlToPdf.jsname:导出页面为pdf格式 **/ import html2Canvas from "html2canvas1.4.1"; import JsPDF from "jspdf2.5.1";const htmlToPdf {get…

APP功能测试思路

一、首先我们拿到一个app的apk包 或者是在testflight下载的app安装包&#xff0c;需要进行安装测试 1.软件安装前&#xff1a;空间不足是否有相应的提示 2.软件安装中&#xff1a;安装过程中是否可以取消、暂停&#xff1b;安装是否可以正常进行&#xff1b;安装空间不足是否…

将yolov8-face里的模型导出到指定opset11

https://github.com/derronqi/yolov8-face 解决办法: 1. 导出前 1 报错内容 (Tensor input, Tensor weight, Tensor bias, tuple of ints stride, tuple of ints padding, tuple of ints dilation, int groups) didnt match because some of the arguments have invalid typ…

2.Ansible的copy模块,我最常用的模块

1. 简述 先从我自身的情况来说&#xff0c;我不是运维人员&#xff0c;并且对linux操作也不是特别熟悉&#xff0c;所以工作中我使用ansible基本就是在平常的自动化部署中&#xff0c;而使用最多的模块就是copy模块。我使用copy模块也主要是来替换生产环境的配置文件。所以&am…

rabbitMQ镜像队列的使用

在rabbitMQ集群中&#xff0c;默认发送消息时&#xff0c;队列默认时在一个节点上存在的。 我们以node01 node02 node03三节点集群为例&#xff0c;在node01声明队列发送消息后&#xff0c;发现&#xff1a; 测试队列只在节点node01上出现。 我们手动停止node01后&#xff0c…

Tomcat外传

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 本篇开始&#xff0c;我…

vue中data为什么是一个函数

vue中的data是一个对象类型&#xff0c;对象类型的数据是按引用传值的&#xff0c;这就会导致所有组件的实例都共享同一份数据&#xff0c;这是不对的&#xff0c;我们要的是每个组件实例都是独立的 为了解决对象类型数据共享的问题&#xff0c;我们需要将 data 定义成一个函数…

C++11线程以及线程同步

C11中提供的线程类std::thread,基于此类创建一个新的线程相对简单&#xff0c;只需要提供线程函数和线程对象即可 一.命名空间 this_thread C11 添加一个关于线程的命名空间std::this_pthread ,此命名空间中提供四个公共的成员函数&#xff1b; 1.1 get_id() 调用命名空间s…

Python自动化测试——元素定位

1.selenium简介 Selenium是一个用于Web应用程序测试的工具。Selenium是直接运行在浏览器中&#xff0c;模拟用户操作web界面。支持多平台&#xff1a;windows、linux、MAC &#xff0c;支持多浏览器&#xff1a;ie、firefox、chrome等浏览器。 2. 启动浏览器 # 导入webdrive…

JavaWeb服务器详解和后端分层解耦

JavaWeb HTTP协议请求数据格式响应数据格式协议解析 Web服务器请求响应请求参数的接收响应 分层解耦IOC&DI入门IOC详解 HTTP协议 超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 特点&#xff1a; 基于TCP协议&#xff1a;面向连接&#xff0c;安全 …

【Android知识笔记】架构专题(二)

分层架构概论 分层的依据是什么? 关注点分离:自下而上,从机器到用户,从抽象到具体,从通用到业务,每一层,各自关注各自的抽象层次。修改与影响:不同层之间的代码或技术方案修改,彼此互不影响。例如 UI 界面从 xml 布局改成 Jetpack Compose 之后,不应该影响数据层。换…

【android开发-03】android中Intent的用法介绍

1&#xff0c;Intent的作用 在Android开发中&#xff0c;Intent的使用非常广泛&#xff0c;包括启动Activity、启动Service、发送广播等。是各组件间交互的一种重要方式&#xff0c;他不仅可以指明当前组件想要执行的动作&#xff0c;还可以在不同组件间传递数据。 Intent可以…

kafka中的常见问题处理

文章目录 1. 如何防⽌消息丢失2. 如何防⽌重复消费3. 如何做到消息的顺序消费4. 如何解决消息积压问题4.1 消息积压问题的出现4.2 消息积压的解决⽅案 5. 实现延时队列的效果5.1 应用场景5.2 具体方案 1. 如何防⽌消息丢失 ⽣产者&#xff1a;1&#xff09;使⽤同步发送 2&…

thinkphp 判断当前页 导航条高亮等方法

ACTION_NAME等表示全局变量,表示当前页面的操作方法 APP_NAME // 当前项目名称 MODULE_NAME //当前模块名称 ACTION_NAME // 当前操作名称 当然,也可以在控制器中,自己定义变量,比如 $this->assign(‘nav’, ‘cp’); 用法示例 <li <if condition" (ACTION_NA…

uniapp使用u-checkbox

当使用uni-app开发时&#xff0c;可以使用u-checkbox组件来实现复选框功能。以下是一个更详细的例子&#xff0c;展示如何在uni-app中使用u-checkbox组件&#xff0c;并介绍一些相关的API用法。 首先&#xff0c;确保已经安装并引入了u-checkbox组件。可以通过在页面的<tem…