类和对象-运算符重载-C++

news2025/3/28 10:49:56

1.加号运算符重载

1.成员函数重载调用

函数的定义部分(这里的person是返回值类型,不是说构造函数)

class person
{
public:
	person operator+(person& p)
	{
		person temp;
		temp.a = this->a + p.a;
		temp.b = this->b + p.b;
		return temp;
	}
	int a;
	int b;
};

函数的调用部分

perpon p3=p1+p2;

这样p3里面的a和b的值就是说p1和p2的a,b的值加起来的。

注意函数名必须是“operator+”,因为这样后面类相加的时候,可以直接简写为p3=p1+p2;

而不用写成person p3=p1.operator+(p2);

2.全局函数重载调用

函数定义部分

person operator+(person& p1, person& p2)
{
	person temp;
	temp.a = p1.a + p2.a;
	temp.b = p1.b + p2.b;
	return temp;
}

函数调用部分

person p3=p1+p2;

注意:

1.函数名必须是“operator+”,因为这样后面类相加的时候,可以直接简写为p3=p1+p2;

而不用写成person p3=p1.operator+(p2);

2.全局函数的重载方式并不能硬套到成员函数方式的重载里,否则会报错。

3.之所以能写成p3=p1+p2;,其中p1+p2是根据形参列表来的。

3.思维发散

除了可以类与类相加,还可以更改重载函数内容,让他可以类与常数相加。

函数定义部分

person operator+(person& p1, int num)
{
	person temp;
	temp.a = p1.a + num;
	temp.b = p1.b + num;
	return temp;
}

函数调用部分

person p3=p1+10;

4.注意

operator+加号重载只能用于类的计算

错误示范

int operator+(int num1, int num2)
{
	int a;
	return a;
}

2.左移运算符重载

1.作用

通常用于输出自定义类

cout<<p<<endl;

其中p是一个类,类内有很多属性,单纯这样输出的话会报错,需要对<<进行重载,来让程序知道给如何输出。

2.与加号运算符区别

左移运算符重载的形参涉及到ostream类(即cout),要写在全局函数,加号运算符重载则可以写在成员函数。

在C++中,左移运算符(<<)重载通常不以成员函数的形式实现,而是作为全局函数(非成员函数)来定义。这样做主要是出于灵活性和一致性的考虑:

  1. 操作数的对称性:当使用左移运算符时,我们通常是在做类似这样的操作 std::cout << object;。这里,std::cout是左侧的操作数,而object是右侧的操作数。如果将<<运算符重载为成员函数,则意味着必须在左侧操作数上调用该函数,即它应该是流对象的一个成员函数。然而,由于标准库中的流类(如ostream)并不知道你的自定义类的存在,你无法直接修改这些流类来添加成员函数。

  2. 访问权限问题:如果你尝试将<<运算符重载为成员函数,则需要在你的类中进行定义。这意味着你需要将这个运算符函数声明为类的成员,从而导致左侧操作数也必须是你自定义的类类型。但实际上,我们希望左侧是一个流对象(比如std::ostream),这会导致设计上的限制。

  3. 友元函数:为了克服上述限制,通常的做法是将<<运算符重载函数作为全局函数实现,并且为了让它能够访问类的私有或保护成员,可以将其声明为对应类的友元函数。这样既保持了封装性,又提供了足够的灵活性来处理输出操作。

3.代码实现

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	person(int a, int b)
		{
		this->a = a;
		this->b = b;
		}
	int a;
	int b;
};
void operator<<(ostream &cout, person &p)
{
	cout << p.a << endl;
	cout << p.b << endl;
}
int main()
{
	person p1(1, 1);
	cout << p1;
	system("pause");
	return 0;
}

4.注意

1.cout做形参必须用引用
  1. 避免拷贝std::ostream 类型的对象通常比较复杂,包含许多内部状态和指向动态分配资源的指针。如果通过值传递(即不使用 &),则会创建该对象的一个副本。对于像流这样的对象,复制它们通常是不可行的,因为这样做会导致多个对象试图管理相同的底层资源,容易引发错误如双重释放等。使用引用可以确保我们操作的是同一个对象,而不是它的副本。

5.引用方式

cout<<p;//p是实例化的类名

6.链式思想改进

假设以这种形式输出的话,会缺少换行

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	person(int a, int b)
		{
		this->a = a;
		this->b = b;
		}
	int a;
	int b;
};
ostream& operator<<(ostream &cout, person &p)
{
	cout << p.a << endl;
	cout << p.b << endl;
	return cout;
}
int main()
{
	person p1(1, 1);
	cout << p1 << endl;;
	system("pause");
	return 0;
}

 

3.递增运算符重载

4.赋值运算符重载

1.类中默认存在的函数

其中operator=用来给类与类之间进行赋值操作

2.operator=存在的问题

其问题和浅拷贝的问题一致

假如使用了指针将数据存放在堆区(利用new)的时候,当另一个对象根据此拷贝出来的时候,与之共用的是一个指针,当程序执行完,调用析构函数(使用delete释放new开辟的空间)的时候,根据先进后出原则,会出现内存重复释放,程序会崩溃。

3.解决方法

(对operator=内部进行修改,使其成为深拷贝)

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int* age ;
	person(int age)
	{
		this->age = new int(age);
	}
	void operator=(person &p)
	{
        //防止内存泄漏,因为在new之前age就有可能有值了
		if (age != NULL)
		{
			delete age;
		}
		age = new int(*p.age);
	}
	~person()
	{
		delete age;
	}
};
int main()
{
	person p1(18), p2(20);
	*p1.age = 18;
	p2 = p1;
	cout << *p1.age << endl;
	cout << *p2.age << endl;
	system("pause");
	return 0;
}

4.新方法存在问题

当出现

p3=p2=p1;

程序会报错,因为p2=p1;之后返回值为空,相当于p3=NULL;

5.解决(添加返回值)

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int* age ;
	person(int age)
	{
		this->age = new int(age);
	}
	person& operator=(person &p)
	{
		if (age != NULL)
		{
			delete age;
		}
		age = new int(*p.age);
		return *this;
	}
	~person()
	{
		delete age;
	}
};
int main()
{
	person p1(18), p2(20),p3(30);
	*p1.age = 18;
	p3=p2 = p1;
	cout << *p1.age << endl;
	cout << *p2.age << endl;
	cout << *p3.age << endl;
	system("pause");
	return 0;
}

其中返回值必须是person&类型,不然会报错,原因:

在 C++ 中,赋值运算符 operator= 的返回值类型通常是类的引用(Person&),而不是类的对象本身(Person)。这是因为返回引用有以下几个重要的原因和好处:


1. 支持链式赋值

C++ 允许链式赋值操作,例如:

p3 = p2 = p1;

这实际上是按以下顺序执行的:

p3.operator=(p2.operator=(p1));
  • 如果 operator= 返回一个对象(Person),那么每次赋值都会创建一个新的临时对象。
  • 如果 operator= 返回引用(Person&),则不会创建额外的临时对象,而是直接返回当前对象的引用,允许链式赋值。

示例:

假设 operator= 返回 Person 而不是 Person&

Person operator=(person &p) {
    if (age != NULL) {
        delete age;
    }
    age = new int(*p.age);
    return *this; // 返回一个新对象
}

在这种情况下,p3 = p2 = p1; 会先调用 p2 = p1,然后返回一个临时对象,再将这个临时对象赋值给 p3。这种行为不仅低效,还容易导致资源管理问题(如内存泄漏)。


2. 避免不必要的拷贝

如果 operator= 返回 Person 对象,那么每次赋值都会触发一次拷贝构造函数或移动构造函数来创建返回的对象。这会导致性能开销,尤其是在对象较大或复杂的情况下。

通过返回引用(Person&),可以避免这些不必要的拷贝操作,提高效率。


3. 符合标准库和语言习惯

C++ 标准库中的所有容器(如 std::string, std::vector 等)都遵循这一约定,即 operator= 返回引用(T&)。这是 C++ 的通用惯例,开发者通常也期望赋值运算符的行为符合这一模式。


4. 示例对比

返回值为 Person 的错误实现

Person operator=(person &p) {
    if (age != NULL) {
        delete age;
    }
    age = new int(*p.age);
    return *this; // 返回一个新对象
}

在这种情况下:

p3 = p2 = p1;
  • p2 = p1 会返回一个临时对象。
  • 然后这个临时对象会被赋值给 p3
  • 由于返回的是一个新对象,可能导致深拷贝、浅拷贝问题,甚至内存泄漏。

返回值为 Person& 的正确实现

Person& operator=(person &p) {
    if (this != &p) { // 检查自赋值
        if (age != NULL) {
            delete age;
        }
        age = new int(*p.age);
    }
    return *this; // 返回当前对象的引用
}

在这种情况下:

p3 = p2 = p1;
  • p2 = p1 返回 p2 的引用。
  • 然后 p3 = p2 使用 p2 的引用完成赋值。
  • 整个过程没有创建任何临时对象,效率更高且更安全。

总结

赋值运算符 operator= 的返回值类型必须是类的引用(Person&),而不是类的对象(Person),原因如下:

  1. 支持链式赋值操作。
  2. 避免不必要的拷贝,提高性能。
  3. 符合 C++ 标准库和语言习惯。
  4. 更好地管理动态内存,避免潜在的资源泄漏或未定义行为。

因此,在你的代码中,operator= 的返回值类型应该是 Person&,而不是 Person

5.关系运算符重载

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int age ;
	bool operator==(person &p)
	{
		if (this->age == p.age)
			return true;
		else return false;
	}
};
int main()
{
	person p1, p2;
	p1.age = 18;
	p2.age = 18;
	if (p1 == p2)
	{
		cout << "相等" << endl;
	}
	else 	
		cout << "相等" << endl;
	system("pause");
	return 0;
}

6.函数调用运算符重载

1.引入

函数调用运算符()也可以重载;

由于重载后使用的方式非常像函数的调用,因此成为仿函数

仿函数没有固定写法,非常灵活;

2.使用场景

1.打印
#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int age ;
	void operator()(string str)
	{
		cout << str << endl;
	}
};
int main()
{
	person p1;
	p1("hello world");
	system("pause");
	return 0;
}

2.运算
#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int age ;
	void operator()(int num1,int num2)
	{
		cout << num1+num2 << endl;
	}
};
int main()
{
	person p1;
	p1(1,2);
	system("pause");
	return 0;
}

3.匿名函数对象

#include<iostream>
using namespace std;
#include<string>
class person
{
public:
	int age;
	void operator()(int num1, int num2)
	{
		cout << num1 + num2 << endl;
	}
};
int main()
{
	person()(1, 2);
	system("pause");
	return 0;
}

好处:匿名函数对象在程序结束时会立即被释放,不占用空间。

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

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

相关文章

2000-2019年各省地方财政耕地占用税数据

2000-2019年各省地方财政耕地占用税数据 1、时间&#xff1a;2000-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政耕地占用税 4、范围&#xff1a;31省 5、指标说明&#xff1a;耕地占用税是地方财政的一种税收&…

从零到一开发一款 DeepSeek 聊天机器人

AI聊天机器人 目标设计方案系统架构技术选型功能模块 实现代码环境配置安装依赖 核心代码API 请求函数主循环函数 功能扩展1. 情感分析2. 多语言支持3. 上下文记忆4. 用户身份识别 总结附录 目标 开发一个智能聊天机器人&#xff0c;旨在为用户提供自然、流畅的对话体验。通过…

S32K144外设实验(五):FTM周期中断

文章目录 1. 概述1.1 时钟系统1.2 实验目的 2. 代码的配置 1. 概述 1.1 时钟系统 FTM的CPU接口时钟为SYS_CLK&#xff0c;在RUN模式下最高80MHz。模块的时钟结构如下图所示。 从上图中可以看出&#xff0c;FTM模块的功能时钟为SYS_CLK&#xff0c;计数器的时钟源可以来源于三…

Android 静态壁纸设置实现方案

提示&#xff1a;Android 平台&#xff0c;静态壁纸实现方案 文章目录 需求&#xff1a;Android 实现壁纸 设置场景 参考资料实现方案直接调用系统 API,WallpaperManager 来实现 wallpaperManager.setResource系统源码分析系统app WallpaperPickerWallpaperPickerActivity ->…

在计算进程D状态持续时间及等IO的时间遇到的一处问题

一、背景 之前的博客 线程每次iodelay监控及D状态开始和结束监控并做堆栈记录-CSDN博客 里&#xff0c;我们讲到了通过内核模块抓取D状态的进程和等IO事件的方法&#xff0c;里面也用到了通过获取rq的symbol&#xff0c;再去获取rq里的rq_clock_task时间的方法&#xff08;内核…

Android11-12-13 替换系统默认壁纸

替换默认壁纸&#xff0c;是客需中再普通不过的需求&#xff0c;这里整理作为笔记记录 文章目录 需求场景 关联资料需求实现拓展总结 需求 客制化客户壁纸需求&#xff0c;替换客户定制的壁纸。 场景 手机-平板相关产品&#xff0c;各种广告机、工控、消费级产品&#xff0c…

Buffer overFolw---Kryo序列化出现缓冲区溢出的问题解决

问题&#xff1a; 由于我的数据量太大&#xff0c;我设置批次为10000万&#xff0c;50w数据大概有400M左右&#xff0c;然后进行spark数据处理时候报错为org.apache.spark.SparkException:Kryo serialization failed:Buffer overFolw.Available:0,rquired 58900977,To …

【Linux】线程基础

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;linux笔记仓 目录 01.背景知识02.线程概念简单使用线程线程调度成本更低 01.背景知识 OS进行内存管理&#xff0c;不是以字节为单位的&#xff0c;而是以内存块为单位的&#xff0c;默认大小为4kb&…

「宇树科技」13家核心零部件供应商梳理!

2025年2月6日&#xff0c;摩根士丹利&#xff08;Morgan Stanley&#xff09;发布最新人形机器人研报&#xff1a;Humanoid 100: Mapping the Humanoid Robot Value Chain&#xff08;人形机器人100&#xff1a;全球人形机器人产业链梳理&#xff09;。 2025年2月20日&#xf…

Spring Boot 项目打包运行

打包成jar包&#xff0c;执行 java -jar 包名&#xff1b; 保证打出的jar包是独立可运行的包&#xff0c;需要xml中添加插件配置 <!-- SpringBoot应用打包插件--> <build><plugins><plugin><groupId>org.springframework.boot</groupId&…

数据结构八股

线性数据结构 数组:数组的内存空间是连续的&#xff0c;随机访问的时间复杂度是01&#xff0c;适用于需要按索引访问元素的场景&#xff0c;但是插入和删除元素较慢&#xff0c;时间复杂度是On链表:链表是由节点组成&#xff0c;节点之间是分散存储的&#xff0c;内存不连续&a…

7.2 控件和组件

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 C#工具箱位于编辑窗口的左侧&#xff0c;它默认内置了大量的控件和组件。控件一般派生于System.Windows.Forms.Control类&#xff0c;显…

transform

http://zhihu.com/question/445556653/answer/3254012065 西科技的文章 视频讲解 小白也能听懂的 transformer模型原理详解 self- attention 多头注意力机制 encoder decoder 机器翻译_哔哩哔哩_bilibili

思库拉水厂开业庆典千人大会回顾

近日,思库拉离子水厂在广州隆重举办了开业盛典,现场汇聚了逾千名嘉宾。此次盛会不仅是对思库拉离子水厂正式投产的庆祝,更是对思库拉品牌未来蓝图的一次展示。 现场氛围热烈,洋溢着浓厚的喜庆气息。参与者来自五湖四海,既有思库拉的忠实拥趸,也有对思库拉产品充满兴趣的潜在消费…

JAVA学习*Object类

Object类 Object类是所有类的父类 类中有一些方法&#xff08;都需要掌握&#xff09; toString()方法 在学习类的对象的时候有介绍过了&#xff0c;当我们重新给此方法就会打印类与对象的信息 equals()方法 在Java中的比较&#xff0c; 如果左右两侧是基本类型变量&#…

基于python脚本实现的打砖块小游戏

目录 1. 打砖块游戏 2. 初始化 Pygame 和设置屏幕 3. 定义游戏对象 3.1 定义玩家操作的paddle 3.2 定义球&#xff08;Ball&#xff09; 3.3 砖块&#xff08;Bricks&#xff09; 4. 游戏主循环 4.1 事件处理 4.2 板子移动 4.3 球移动和碰撞检测 4.4 绘制游戏对象 …

地理信息系统(GIS)在智慧城市中的40个应用场景案例

在智慧城市发展进程中&#xff0c;地理信息系统&#xff08;GIS&#xff09;作为关键技术之一&#xff0c;正扮演着不可或缺的角色&#xff0c;堪称智慧城市的神经中枢。通过空间数据分析优化城市管理&#xff0c;GIS技术为智慧城市的构建提供了强大的支持。 本文分享了GIS在智…

XSS Game(DOM型) 靶场 通关

目录 靶场网址 Ma Spaghet! 分析 解题 Jefff 分析 解题 方法一 方法二 Ugandan Knuckles 分析 解题 Ricardo Milos 分析 解题 Ah Thats Hawt 分析 解题 方法一 方法二 Ligma 分析 解题 ​ Mafia 分析 解题 方法一&#xff1a;构造函数 方法二&#xf…

【大模型基础_毛玉仁】3.5 Prompt相关应用

目录 3.5 相关应用3.5.1 基于大语言模型的Agent3.5.2 数据合成3.5.3 Text-to-SQL3.5.4 GPTs 3.5 相关应用 Prompt工程应用广泛&#xff0c;能提升大语言模型处理基础及复杂任务的能力&#xff0c;在构建Agent、数据合成、Text-to-SQL转换和设计个性化GPTs等方面不可或缺。 . …

deepseek搭建本地私有知识库dify安装介绍docker compose图文教程

按照官方介绍&#xff0c;打开教程安装。下载源码&#xff0c; # 假设当前最新版本为 0.15.3 git clone https://github.com/langgenius/dify.git --branch 0.15.3 进入docker目录&#xff0c; cd dify/docker 网络科学的直接执行命令就可以了。 docker compose up -d 镜…