C++ 运算符重载为成员函数

news2024/11/26 20:34:26

运算符重载实质上就是函数重载,重载为成员函数,他就可以自由访问本类的数据成员。实际使用时,总是通过该类的某个对象来访问重载的运算符。

如果是双目运算符,左操作数是对象本身的数据,由this指针指出,右操作数则需要通过运算符重载函数的参数表来传递;如果是单目运算符,操作数由对象的this指针给出,就不再需要任何参数。

(1)双目运算符的重载
对于双目运算符B,如果要重载为类的成员函数,使之能够实现表达式a B c ,其中a1是A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参类型为c所属的类型。经过重载之后,表达式a B c 就相当于调用a.operator B(c)
(2)单目运算符的重载
对于前置单目运算符U,如“-”(负号)等,如果要重载为类的成员函数,用来实现表达式U a,其中a为A类对象,那么U应当重载为A类的成员函数,函数没有形参。经过重载之后,表达式U a就相当于调用a.operator U()
对于后置运算符“++”和“–”来说,如果将它们重载为类的成员函数,用来实现表达式a++或者a--,其中a为A类对象,那么运算符就应当重载为A类的成员函数,这时函数要带有一个整型(int)形参。重载之后,表达式a++a--就相当于调用a.operator++(0)a.operator--(0)。这里的int类型的参数在运算中不起任何作用,只是用于区别后置++、–和前置++、–。

【例1】两个对象相加的实现体,即两个对象相加的原理。就是把两个对象中的数据成员依次进行相加。

class A
{
public:
	A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {}
	int GetI()const
	{
		return m_i;
	}
	int GetJ()const
	{
		return m_j;
	}
	void Add(A& s)
	{
		cout <<"("<< this->m_i + s.m_i << "," << this->m_j + s.m_j << "," << this->m_k + s.m_k<<")"<<endl;
		
	}

private:
	int m_i;
	int m_j;
	int m_k;
};
void main()
{
	A a(2, 5, 7);
	A b(5, 7, 9);
	a.Add(b); //a+b
}

运行结果:
在这里插入图片描述
【例2】两个对象相乘的实现体,即两个对象相乘的原理。就是把两个对象中的数据成员依次进行相乘。

class A
{
public:
	A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {}
	int GetI()const
	{
		return m_i;
	}
	int GetJ()const
	{
		return m_j;
	}
	void fn(A& s)
	{
		cout << "(" << this->m_i * s.m_i << "," << this->m_j * s.m_j << "," << this->m_k * s.m_k << ")" << endl;
	}
private:
	int m_i;
	int m_j;
	int m_k;
};
void main()
{
	A a(2, 5, 7);
	A b(5, 7, 9);
	a.fn(b); //a * b

}

运行结果:
在这里插入图片描述
以上两个个例子中,在执行Add函数a.Add(b);或者fn函数a.fn(b); 之前,在执行结果还没有出来的情况下,我们是不知道这两个函数到底是干什么的,这导致程序的可读性不好。虽然上面两个例子中通过函数的调用实现了我们所需要实现的功能,但是这样的写法不如直接写成运算符可以直观的表达出我们要现实的功能是什么。所以引出运算符重载。

运算符重载的原因:
①想让当前的对象像基本数据类型去操作。
②把对象的运算写成运算符的形式比写成函数调用的形式可读性更强。

【例3】把运算符“+”重载为成员函数

class A
{
public:
	A(int i = 0) :m_i(i) {}
	A operator+(A& s)
	{
		cout << "调用函数operator +()" << endl;
		return this->m_i + s.m_i;//把A类中的m_i和B类中的m_i相加,this指针指向当前对象a中的数据成员m_i的地址
		//此处返回值为int型,是因为构造函数可以进行类型转换
	}
	void print()
	{
		cout << m_i << endl;
	}
private:
	int m_i;
};
int main()
{
	A a(5);
	A b(6);

	//a + b;//相当于a.operator+(b)  //+(a.b)
	(a + b).print();/*遇到+,先看类中有没有重载加法运算符,如果重载了,去调用这个+的重载函数,
	                      把第一个操作数a当作this指针传给这个重载函数,把右操作数b当作实参传给+的重载函数的形参*/

	A c;
	c = a.operator+(b);
	c.print();
	
	return 0;
}

运行结果:
在这里插入图片描述
结果分析:
在程序中,遇到“+”,先看此处的“+”是不是加法运算符重载,如果重载了,去调用这个“+”的重载函数,在上述程序,主函数中遇到的a+b,这里的“+”是加法运算符重载,所以去调用A operator+(A& s)重载函数,把第一个操作数a当作this指针传给这个重载函数,把右操作数b当作实参传给这个重载函数的形参A& s。在重载函数A operator+(A& s)的函数体内将A类对象a中的m_i和B类对象b中的m_i相加的值返回给A类的构造函数的形参,然后通过初始化列表给A类的数据成员m_i进行赋值为A类中对象m_i的值和B类对象m_i的值相加的结果。然后用这个相加后的对象去调用print函数,输出相加的结果。

因为运算符重载的实质为函数重载,所以可以把a+b;写为a.operator+(b);这两种写法完全相同。

减法运算符“-”的重载与加法运算符“+”的重载同理。

【例4】默认赋值运算符重载函数

class A
{
public:
	A(int i = 0) :m_i(i) {}
	
	void print()
	{
		cout << m_i << endl;
	}
private:
	int m_i;
};
int main()
{
	A a(5);
	A b(6);

	(a = b).print();
	
	return 0;
}

运行结果:
在这里插入图片描述
结果分析:
我们从程序中可以看出当前运算符“=”并没有重载,但是结果却没错,说明在类中给了一个默认的赋值运算符重载函数。默认的赋值运算符重载函数就是把当前的右边对象的数据成员的值依次赋值给左边对象的数据成员。这里的默认赋值重载函数是浅赋值。如果把这个默认赋值运算符重载函数写出来,就是这样:

A& operator(const A& s)
{
	if(this==&s)//判断自赋值
		return *this;
	m_i=s.m_i;
	return *this;
	
}

所以 (a = b).print();相当于是a.operator=(b),把a当作this指针传给这个重载函数,把b当作实参传给这个重载函数的形参。上面这个默认的赋值运算符重载函数的返回类型为引用类型,是因为如果使用值类型返回就会产生拷贝构造函数,比较麻烦,而这里由当前运算符组成的表达式可以放在=的左边,它具有相应的内存空间,所以使用引用作为默认的赋值运算符重载函数的返回类型。所以,最后的 return *this;返回的就是当前对象a的数据成员m_i的值。

【例5】单目运算符“++”重载为成员函数。

class A
{
public:
	A(int i = 0) :m_i(i) {}

	void print()
	{
		cout << m_i << endl;
	}

	A operator++(int)  //为了实现++的重载,语法规定,在后++的时候,参数里面写int,没有实际的意义,不用传参
	{
		cout << "调用函数operator++" << endl;
		return m_i++;
	}
	A& operator++()
	{
		cout << "调用函数++operator" << endl;
		m_i = m_i + 1;
		return *this;
	}
private:
	int m_i;
};

void main()
{
	A a(6);
	A b(3);
	A c(20);

	//后置++
	cout << "(a++)表达式的值:"<<endl;
	(a++).print();//相当于a.++()  //a++表达式的值是a没加以前的值6  a的值是a加过1之后的值7,最终返回的是表达式的值
	//(a++)这个表达式在执行过程中有两个结果
	cout << "a的值:" << endl;
	a.print();


	//前置++	
	cout << "(++b)表达式的值:" << endl;
	(++b).print(); //b.++()  ++b表达式的值是b加过1之后的值,b的值也是b加过1之后的值
	//因为++b表达式的值和b的值是相同的,所以没有必要给++b这个表达式开辟空间,直接从b的内存单元中将这个值取出来,所以返回类型可以不用值类型,而是使用引用的返回类型
	cout << "b的值:" << endl;
	b.print();

}

运行结果:
在这里插入图片描述
结果分析:
①语法规定,在后置++的时候,参数里面写int,没有实际的意义,不用传参,它的目的是为了与前置++构成函数重载。
②后置++:(a++)表达式的值是a没加以前的值,a的值变为了a+1以后的值,所以后置++表达式有两个不同的值,但是最终返回的是表达式的值,因此,对于将后置++运算符重载为成员函数时的返回类型为值类型。
③前置++ :(++b)表达式的值和b的值一样,都是b+1以后的值,所以前置++表达式的值只有一个,因为++b表达式的值和b的值是相同的,所以没有必要给++b这个表达式开辟空间,直接从b的内存单元中将这个值取出来,所以返回类型可以不用值类型,而是使用引用的返回类型。

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

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

相关文章

1. 软件生命周期C/S、B/S 架构

目录 1. 软件生命周期 2. 面向对象 2.1 面向对象分析 2.2 面向对象设计 2.3 面向对象编程 3. C/S、B/S 架构 3.1 CS 架构 3.2 BS 架构 1. 软件生命周期 软件生命周期中划分为可行性研究、需求分析、概要设计、详细设计、实现、组装(集成)测试、 确认测试、使用、维护…

【Linux】进程间通信——system V共享内存

目录 写在前面的话 System V共享内存原理 System V共享内存的建立 代码实现System V共享内存 创建共享内存shmget() ftok() 删除共享内存shmctl() 挂接共享内存shmat() 取消挂接共享内存shmdt() 整体通信流程的实现 写在前面的话 上一章我们讲了进程间通信的第一种方式…

kubeadml 安装 k8s

目录 一&#xff1a;kubeadml 安装 k8s 1、网络环境 2、 环境准备 3、 所有节点安装docker 4、所有节点安装kubeadm&#xff0c;kubelet和kubectl ​5、部署K8S集群 6、测试 二&#xff1a; 部署 Dashboard 一&#xff1a;kubeadml 安装 k8s 1、网络环境 master&am…

VBA遍历Wrod所有表格每个单元格,单元格未尾两个回车替换

一、遍历 word中遍历所有表格的每个单元格。因为在单元格时会常出错。浪费了不少时间。 Sub a()Dim doc As Document, tb As Table, ce As cellDim rng As Range, p As ParagraphSet doc ActiveDocumentFor Each tb In doc.TablesFor Each ce In tb.Range.Cells 关键处就是这里…

作为程序猿,怎么维护自己的电脑?

我的电脑是联想拯救者R7000 内存&#xff1a;16G CPU &#xff1a;AMD R5 外存&#xff1a;500G。买这台电脑是个意外。情况是这样的&#xff0c;面试去了一家外包公司&#xff0c;入职当天&#xff0c; HR问我&#xff1a;你的电脑呢&#xff1f; 我&#xff1a;没有发给我啊 …

十一、ESP32加快240x240显示二维码

1. 效果 非常快速的显示出二维码 2. 原理 2.1 之前的方式(慢)

MySQL日期常见的函数

-- 获取当天日期 -- 2023-06-20 select curdate();-- 获取当天年月日时分秒 select now();-- 日期运算 -- 2024-06-20 17:04:17 select date_add(now(),interval 1 year);-- 日期比较 -- 0 select datediff(now(),now());-- 日期MySQL对于日期类型数据如何查询 -- 获取指定日期…

vcode开发go

配置环境变量 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct 创建文件夹 mkdir hello cd hello go mod init hello 安装所有"all" matched packages go mod tidy

Linux文件属性与权限管理(可读、可写、可执行)

Linux把所有文件和设备都当作文件来管理&#xff0c;这些文件都在根目录下&#xff0c;同时Linux中的文件名区分大小写。 一、文件属性 使用ls -l命令查看文件详情&#xff1a; 1、每行代表一个文件&#xff0c;每行的第一个字符代表文件类型&#xff0c;linux文件类型包括&am…

Python编程从入门到实践练习第五章:if语句和条件测试

目录 一、条件测试1.1 检测多个条件&#xff08;and / or&#xff09;1.2 检测特定值是否包含在列表中1.3 if语句结构 二、if语句处理列表2.1 判断列表是否为空2.2 练习题代码输出 一、条件测试 1.1 检测多个条件&#xff08;and / or&#xff09; 所用关键词 and : 两个条件…

C++ 多态 虚函数表

文章目录 简易抽象理解多态多态的具体实现虚函数的定义虚函数的重写重定义&#xff08;隐藏&#xff09;、重载 、重写&#xff08;覆盖&#xff09;区别C11 override 和 final 关键字抽象类的定义接口继承和实现继承多态的原理&#xff1a;虚函数表单继承和多继承关系的虚函数…

Nginx可视化Nginx-gui

Github&#xff1a;GitHub - onlyGuo/nginx-gui: Nginx GUI Manager 运行方式支持docker、window 下载后压缩&#xff0c;直接运行startup.bat 默认账号密码&#xff1a;admin/admin

Flutter iOS 集成使用 flutter boost

在 Flutter项目中集成完 flutter boost&#xff0c;并且已经使用了 flutter boost进行了路由管理&#xff0c;这时如果需要和iOS混合开发&#xff0c;这时就要到 原生端进行集成。 注意&#xff1a;之前建的项目必须是 Flutter module项目&#xff0c;并且原生项目和flutter m…

CorelDRAW2023矢量制图支持Windows和macOS系统安装使用

CorelDRAW Graphics Suite - 矢量制图专为企业打造的矢量设计作图软件CorelDRAW Graphics Suite套装&#xff0c;支持Windows和macOS系统安装使用。使用 CorelDRAW Graphics Suite 2023&#xff0c;打破创意障碍&#xff0c;以自己的风格进行自由创作。与全球数百万仰赖 CorelD…

睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程

简介&#xff1a; 睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程 支持分享海报 支持暗黑模式 包含了音频数据 最近很火的助眠小程序&#xff0c;前端vue&#xff0c;可以打包H5&#xff0c;APP&#xff0c;小程序 后台可以设置流量主广告&#xff0c;非常不错的源码 代码完…

Linux操作系统知识点总结(二)

总结&#xff08;一&#xff09;链接Linux操作系统知识点总结&#xff08;一&#xff09;&#xff08;附VMware、CentOS以及finalshell的安装教程&#xff09;_你好&#xff0c;明天&#xff0c;&#xff0c;的博客-CSDN博客 43. 由于虚拟机的 Linux系统的IP地址是通过DHCP服务…

前端CSS文字阴影text-shadow记录

前端CSS文字阴影text-shadow记录 一、文字阴影 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Doc…

【基于IDEA + Spark 3.4.1 + sbt 1.9.3 + Spark MLlib 构建逻辑回归鸢尾花分类预测模型】

逻辑回归进行鸢尾花分类的案例 背景说明&#xff1a; 基于IDEA Spark 3.4.1 sbt 1.9.3 Spark MLlib 构建逻辑回归鸢尾花分类预测模型&#xff0c;这是一个分类模型案例&#xff0c;通过该案例&#xff0c;可以快速了解Spark MLlib分类预测模型的使用方法。 依赖 ThisBui…

“算法详解”系列第3卷贪心算法和动态规划出版

“算法详解”系列图书共有4卷&#xff0c;目前1到3卷已经出版。最新出版的是第3卷—贪心算法和动态规划。 算法详解 卷3 贪心算法和动态规划 “算法详解”系列图书共有4卷&#xff0c;本书是第3卷—贪心算法和动态规划。其中贪心算法主要包括调度、最小生成树、集群、哈夫曼编…