[C++] 类和对象:运算符重载


前言:

在C++中,运算符重载是一种强大的特性,它允许我们重新定义已有的运算符,以便用于用户自定义的数据类型。通过运算符重载,我们可以使得我们自定义的类对象像内置类型一样进行运算,这为编写清晰、简洁且易于理解的代码提供了便利。

为什么要进行运算符重载?

在学习运算符重载之前:
我们对于内置类型的运算是这样的:int = int + int;
但是我们对于自定义类型的类无法使用简单的运算符进行运算,我们需要特别得去写一个函数或者一段过程性代码来实现这个功能。

使用重载运算符:

  • 比如说在一个日期类内有该重载
bool operator==(const Date& d2)
{
    return _year == d2._year;
        && _month == d2._month
        && _day == d2._day;	
}

该代码就是对“”运算符的重载,之后通过使用”“就可以对比日期类的两个对象(根据该运算是否有意义来决定)。
使用时的代码就是如此:d1 == d2

运算符重载的语法

函数原型:**返回值类型 operator操作符(参数列表)**
注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐

藏的this

  • . * :: sizeof ? : . 注意以上5个运算符不能重载。这个经常在笔试选择题中出

现。

以日期类进行举例:

①非成员函数重载:

bool operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
        && d1._month == d2._month
        && d1._day == d2._day;
}

d1.operator(d2)或者d1 == d2都可以实现;
由于在类外实现重载,所以没有this指针,所以可以用类内,但是C++的语法会对此进行优化直接使用d1 == d2也可以完成。

②成员函数重载:

bool operator==(const Date& d2)
{
    return _year == d2._year;
        && _month == d2._month
        && _day == d2._day;
}

类内时,左操作数为this,指向调用函数的对象,可以直接用 d1 == d2进行运算。

当运行到使用重载运算符的时候就会进行调用重载函数:
image.png
image.png

赋值运算符重载

赋值运算符重载格式

参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
**检测是否自己给自己赋值
返回*this **:要复合连续赋值的含义

//作为成员函数,赋值运算符重载函数:
// 用Date类型引用返回使得可以连续赋值
Date& operator=(const Date& d)
{
    if(this != &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}

为什么赋值运算符重载函数为成员函数?

当尝试将赋值运算符重载函数作为静态函数进行定义时:
image.png
image.png


在C++中,赋值运算符 = 被设计为类的成员函数,这是因为它需要访问类的内部状态,并且需要能够处理自赋值的情况(即对象赋值给自己)。当你尝试将赋值运算符重载为全局函数时,会出现问题,原因如下:

  1. 成员访问权限:作为全局函数,赋值运算符将无法访问类的非公共(private或protected)成员变量。成员函数可以直接访问这些成员,因为它们是类的一部分。
  2. this指针:成员函数有一个隐式的指针 this,它指向调用该成员函数的对象。在成员函数内部,this 指针允许你访问对象的成员变量和其它成员函数。全局函数没有 this 指针,因此无法访问特定对象的状态。
  3. 自赋值保护:成员函数版本的赋值运算符可以检查自赋值,即对象赋值给自己。这可以通过比较 this 指针和传入的右值的地址来实现。全局函数没有 this 指针,因此无法进行这种检查。
  4. 语法要求:C++ 语法要求赋值运算符 = 必须是类的成员函数。尝试将其定义为非成员函数会导致编译错误,因为编译器期望赋值运算符是类的成员。
  5. 语义问题:赋值运算符的语义是将一个对象的值设置为另一个对象的值。作为成员函数,它清楚地表达了这一点,因为它是在对象上直接调用的。作为全局函数,这种语义就不那么明确了。
  6. 重载规则:C++ 的运算符重载规则限制了某些运算符(包括赋值运算符)只能作为成员函数重载。这是为了保持语言的一致性和防止潜在的错误使用。

因此,当尝试将赋值运算符重载为全局函数时,编译器会报错,因为它违反了C++的规则和赋值运算符的预期行为。正确的做法是将其作为类的成员函数来重载,以确保正确的访问权限、自赋值保护以及符合C++的语法和语义要求。

默认赋值运算符重载

  • 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。、(值拷贝/浅拷贝 类似Date )。
  • 这个默认的赋值运算符会进行成员到成员的简单赋值,也就是逐个复制每个非引用、非指针成员变量的值。
  • 如果类中包含了其他自定义类型作为其成员变量,并且这些自定义类型重载了赋值运算符 =,那么在进行类实例的赋值操作时,编译器会尝试调用这些成员变量类型的赋值运算符来完成赋值(MyQueue)。

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必
须要实现。

前置++和后置++重载

前置++

// 由于this指向的是函数外定义的对象,所以不会销毁,使用引用返回提高效率
Date& operator++()
{
    _day += 1;
    return *this;
}

++d1执行的时候将会返回++后的日期。

后置++

Date operator++(int)
{
    Date temp(*this);
    _day += 1;
    return temp;
}

d1++:后置++的效果是对++的对象进行处理后返回处理前的数值,所以在函数内定义一个对象进行储存++前的数据,不使用引用返回,使得在返回后构造函数,使程序正常进行。

由于前置++和后置++的源代码原本是没区别的,所以为了区别这两个,祖师爷用一种机制来进行区分使用:后置++运算符重载时,需要声明一个额外的int类型的参数,这个参数在实际使用时并不需要由程序员显式传递,而是由编译器在调用后置++时自动传递。


# 对象的流插入和流输出 ![image.png](https://img-blog.csdnimg.cn/img_convert/9e5c619629b007e8e08be6fee14cd4fe.png) ```cpp class Date { friend istream& operator>>(istream& in, Date& d); friend ostream& operator<<(ostream& out, const Date& d); } ``` 用引用返回保证可以连续的进行使用,类似:`in >> d1 >> d2 >> d3;` ## 流输出 ( << ) 为了按照`out << d`的形式来进行使用重载运算符,所以用在类中用友元函数,这样既避免了this指针,还可以使该函数可以使用成员变量,以达到使用这种操作格式的目的。 ```cpp // 在类外实现 ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日"; return out; } ```

流输出

和流输入一样类内用友元函数。

istream& operator>>(istream& in, Date& d)
{
	int year, month, day;
	in >> year >> month >> day;

	if (month > 0 && month < 13
		&& day > 0 && day <= d.GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}

	return in;
}

取地址及const取地址操作符重载

对象取地址

class Date 
{
public:

    // 重载 operator&(),返回指向 Date 的常量指针  
    const Date* operator&() const 
    {
        // 这里通常应该直接返回 this,但出于示例的目的,我们可能会做一些检查或其他逻辑  
        // 但实际上,直接返回 this 是最简单和最直接的方式  
        return this;
    }

    // 注意:如果你还想要一个非 const 版本的 operator&(),你可以这样重载它  
    Date* operator&() 
    {
        // 同样的,直接返回 this  
        return this;
    }
};
int main() 
{
   Date date;

   // 使用重载的 operator&()  
   const Date* constPtrToDate = date.operator&(); // 使用成员函数调用方式  

   // 但是通常你会这样写,因为编译器会隐式地使用 & 运算符  
   const Date* anotherConstPtrToDate = &date;

   // 注意:如果你重载了非 const 版本的 operator&(),你也可以这样使用  
   Date* ptrToDate = &date; // 或者 ptrToDate = date.operator&();  

   // ... 其他代码 ...  

   return 0;
}

const this

例如在类中有以下函数:

void Print()
{
	cout << _year << _month << _day << endl;
}

当我们想在主函数中调用该函数时,有两个不同的对象,const d1(2021.2.3),d2(2031, 2,1),当我们直接调用时会发现const d1无法调用Print,这时候就涉及到了权限的放大。
d1 和 d2是作为this指针传递的,Print中的this指针是Date* this类型的,而d1是一个const Date*型的,所以无法传入d2进行调用函数。
这时候就涉及到了const this当使用const修饰this后,就可以将之前d1的权限放大变为权限平移,d2的权限平移变成权限缩小,这样就都可以实现调用。
实现代码格式如下:

// 将const加在函数后面
void Print() const
{
	cout << _year << _month << _day << endl;
}

在使用的过程中要注意:
①要修改对象成员变量的函数不能加;
②只要成员函数内部不修改成员变量,就都应该加const,这样const对象和普通对象都可以调用。
③这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis(安装及配置)

1.什么是redis Redis 全称 Remote Dictionary Server&#xff08;即远程字典服务&#xff09;&#xff0c;它是一个基于内存实现的键值型非关系&#xff08;NoSQL&#xff09;数据库&#xff0c;由意大利人 Salvatore Sanfilippo 使用 C 语言编写。 2.优势 性能极高&#xff…

靠谱二次元行研组:2024年国产动画番剧趋势报告

靠谱二次元行研组 根据靠谱二次元&#xff08;公众号ID&#xff1a;kpACGN&#xff09;统计&#xff0c;2023年全年累计在播动画番 剧超140部&#xff0c;有更新的年番超过15部。其中有123部国产动画番剧新开播&#xff0c;涉 及110个IP&#xff0c;80家制作公司&#xff0c;新…

Cloudera的简介及安装部署

简介 Cloudera是一家位于美国的软件公司&#xff0c;成立于2008年&#xff0c;专注于为企业客户提供基于Apache Hadoop的软件、支持、服务以及培训。Cloudera的开源Apache Hadoop发行版&#xff0c;即Cloudera Distribution including Apache Hadoop&#xff08;CDH&am…

基于YOLOv5的火焰目标检测(代码+数据集+训练好的模型)

基于YOLOv5的火焰目标检测项目是一个旨在实时识别和定位视频或图像中火焰区域的计算机视觉应用。YOLOv5是YOLO&#xff08;You Only Look Once&#xff09;系列目标检测模型的一个高效版本&#xff0c;以其快速、准确且易于部署的特点而受到青睐。 技术背景 YOLOv5&#xff1…

C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

C++ list介绍(迭代器失效)

一、常用接口 reverse逆置 sort排序&#xff08;默认升序&#xff09; 仿函数greater<int> merge合并&#xff0c;可以全部合并&#xff0c;也可以一部分合并 unique&#xff1a;去重&#xff08;先排序&#xff0c;再去重&#xff09; remove&#xff1a;删除e值&#…

Django之rest_framework(六)

一、GenericViewSet类的使用 继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于视图的开发 1.1、代码 from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from rest_framework …

基于Springboot+Vue+Java的校园资料分享平台

&#x1f49e; 文末获取源码联系 &#x1f649; &#x1f447;&#x1f3fb; 精选专栏推荐收藏订阅 &#x1f447;&#x1f3fb; &#x1f380;《Java 精选实战项目-计算机毕业设计题目推荐-期末大作业》&#x1f618; 更多实战项目~ https://www.yuque.com/liuyixin-rotwn/ei3…

【前端热门框架【vue框架】】——对组件进行更加简洁合理的处理和解释(一)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;程序员-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

css mix-blend-mode 层叠样式属性各类效果

官方给出的定义是&#xff1a;mix-blend-mode css 属性描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合。 通俗来讲&#xff0c;就是一张图片跟它的父级元素背景色的融合方式。 大致分为以下16种&#xff1a; mix-blend-mode: normal; mix-blend-mode: m…

QT--day3

1、mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> #include<QIcon> //图标类 #include<QLabel> //标签类 #include<QMovie> //动图类 #include<QLineEdit> //行编辑器类 #include<QPushButton> //按钮类 #include…

美团二面:SpringBoot读取配置优先级顺序是什么?

引言 Spring Boot作为一种轻量级的Java应用程序框架&#xff0c;以其开箱即用、快速搭建新项目的特性赢得了广大开发者的青睐。其核心理念之一就是简化配置过程&#xff0c;使开发者能够快速响应复杂多变的生产环境需求。为了实现这一点&#xff0c;Spring Boot支持丰富的外部…

智慧旅游推动旅游服务智慧化转型:借助智能科技的力量,实现旅游资源的精准匹配和高效利用,为游客提供更加便捷、舒适的旅游环境

目录 一、引言 二、智慧旅游的定义与特点 &#xff08;一&#xff09;智慧旅游的定义 &#xff08;二&#xff09;智慧旅游的特点 三、智能科技在旅游服务中的应用 &#xff08;一&#xff09;大数据分析助力旅游决策 &#xff08;二&#xff09;人工智能实现个性化推荐…

【C++】map和set的基础详解

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

【使用ChatGPT的API之前】OpenAI API提供的可用模型

文章目录 一. ChatGPT基本概念二. OpenAI API提供的可用模型1. InstructGPT2. ChatGPT3. GPT-4 三. 在OpenAI Playground中使用GPT模型-ing 在使用GPT-4和ChatGPT的API集成到Python应用程序之前&#xff0c;我们先了解ChatGPT的基本概念&#xff0c;与OpenAI API提供的可用模型…

SpringBoot Actuator未授权访问漏洞的解决方法

1. 介绍 Spring Boot Actuator 是一个用于监控和管理 Spring Boot 应用程序的功能模块。它提供了一系列生产就绪的功能&#xff0c;帮助你了解应用程序的运行状况&#xff0c;以及在运行时对应用程序进行调整。Actuator 使用了 Spring MVC 来暴露各种 HTTP 或 JMX 端点&#x…

WOA-SVM多变量分类预测|基于鲸鱼优化算法的支持向量机|Matalb

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

stm32f103zet6_RTC_1_介绍

RTC简介 实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器&#xff0c;在相应软件配置下&#xff0c;可 提供时钟日历的功能。 修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域&#xff0c;即在系统复…

工采电子国产D类音频放大器iML6602可以替代TPA3118

iML6602是一款高集成度、高效率的双声道D类音频功率放大器&#xff1b;支持BTL和PBTL两种模式输出&#xff0c;供电电压范围4.5V ~ 26V&#xff1b;双通道BTL模式下输出功率 230W&#xff08;8Ω&#xff0c;24V&#xff0c;THDN0.1%&#xff09;单通道PBTL模式下可以输出60W&a…
最新文章