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.hqwc.cn/news/236773.html

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

相关文章

百度贴吧大战:国内工厂爆改RTX 4090游戏显卡!用于AI模型训练

最近这英伟达芯片禁售这事&#xff0c;可谓搅的 AI 圈、游戏圈风起云涌&#xff0c;前不久我们刚刚报道完老黄如法炮制再次推出三款中国特供版 AI 芯片&#xff0c;后脚我们就发现国内公司也上有政策下有对策&#xff0c;百度贴吧显卡吧有网友在前线发来报道&#xff1a; 国内 …

提升APP软件的用户体验方法

提升APP软件的用户体验是确保用户满意度和应用成功的关键。以下是一些方法&#xff0c;可以帮助提升APP的用户体验&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.简洁的用户界面设计&#xff1a; …

女子曝被医生男友下药流产,男子被警方行拘10日,医院停职!

近日&#xff0c;一则关于女子被医生男友暗中下堕胎药导致流产的消息引起了广泛关注。经过一个多月的报案和调查&#xff0c;如今有了新的进展。 11月30日&#xff0c;王女士向华商报大风新闻记者证实&#xff0c;北京中关村某医院涉事医生已被行政拘留&#xff0c;被处以行政拘…

【Linux】I/O多路转接技术

I/O多路转接技术 一、I/O多路转接之select1、select函数2、fd_set的相关内容3、如何在代码中高效的使用select函数4、select服务器5、select的优缺点6、select的适用场景 二、I/O多路转接之poll1、poll函数2、struct pollfd结构2、poll服务器3、poll的优缺点 三、I/O多路转接之…

hbase thrift2 jar包冲突导致启动失败问题排查记录

1、启动命令 ${HBASE_HOME}/bin/hbase-daemon.sh start thrift2 2、异常情况 hbase-root-thrift2-hdfs-test07.yingzi.com.out异常日志&#xff1a; Exception in thread "main" java.lang.AbstractMethodError: org.apache.hadoop.metrics2.sink.timeline.Hadoo…

在cmd下查看mysql表的结构信息

我提前已经在mysql数据库中创建了一个表&#xff1a; 在cmd下&#xff0c;登录mysql以后&#xff0c;使用命令describe 表名、或者explain 表名可以查看表结构信息。但在实践中&#xff0c;查看表结构&#xff0c;多用describe命令&#xff0c;而查看执行计划用explain。 例…

element ui el-date-picker日期时间选择器 设置只能选择不大于30天时间范围

需求&#xff1a;要求日期时间选择器只能选择最多32天&#xff0c;其他日期为不可点击状态。 日期组件type为daterange或者datetimerange都生效 实现&#xff08;vue2.x&#xff09;&#xff1a; 通过属性picker-options html <el-date-pickerv-model"dateTime&qu…

神经网络模型数字推理预测

MNIST数据集 MNIST是机器学习领域 最有名的数据集之一&#xff0c;被应用于从简单的实验到发表的论文研究等各种场合。 实际上&#xff0c;在阅读图像识别或机器学习的论文时&#xff0c;MNIST数据集经常作为实验用的数据出现。 MNIST数据集是由0到9的数字图像构成的。训练图…

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之linux存储管理(2)》(18)

《Linux操作系统原理分析之linux存储管理&#xff08;1&#xff09;》&#xff08;17&#xff09; 6 Linux存储管理6.2 选段符与段描述符6.2.1 选段符6.2.2 段描述符6.2.3 分段机制的存储保护 6.3 80x86 的分页机制6.3.180x86 的分页机制6.3.2 分页机制的地址转换6.3.3 页表目录…

⭐ Unity + ARKIT 介绍 以及 平面检测的实现

在AR插件中&#xff0c;ARKIT是比较特殊的一个&#xff0c;首先他在很多追踪上的效果要比其他的AR插件要好&#xff0c;但是只能在IOS系统设备上运行。 1.首先ARKIT在最新版Unity已经集成在AR Foundation中&#xff0c;那我们就需要ARSession 和ARSessionOrigin这两个重要组件…

年终好价节买什么好?高效实用、高性价比的的数码好物推荐

前段时间&#xff0c;“淘宝双12不再举办”的话题上了热搜&#xff0c;改成了“淘宝年终好价节”。从“双12”到“好价节”&#xff0c;背后意味着大众跳出了一味追求低价的“买买买”的怪圈&#xff0c;转变为更追寻价好质优的商品。错过双11的消费者可以趁这个时间抓紧入手收…

深度学习手势识别 - yolo python opencv cnn 机器视觉 计算机竞赛

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…