C++之类和对象--赋值运算符重载和const成员函数

目录

1.赋值运算符重载

1.1运算符重载

 1.2赋值运算符重载

1.3其它特性 

2.const成员函数

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


hello,欢迎大家来到小恶魔频道,今天讲解的是C++里面的赋值运算符重载以及const成员函数

1.赋值运算符重载

1.1运算符重载

运算符重载是一种编程语言特性,它允许开发者为已有的运算符提供自定义的实现。这意味着你可以改变某些运算符在你自定义的类或数据类型上的行为。比如,你可以定义加号运算符(+)如何在你自定义的数据结构上进行运算

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
int main()
{Date d1(2024, 4, 22);Date d2(2024, 1, 1);return 0;
}

像以上代码

在这个代码中,我们如何比较d1和d2的是否相同呢?

常规方法:写一个函数去比较

bool Compare(const Date& dt1,const Date& dt2)
{return dt1._year == dt2._year&& dt1._month == dt2._month&& dt1._day == dt2._day;
}

 运行完后发现为0,也就是不相等

那么如果接下来我们想要直接比较d1==d2

这时候就会运用到运算符重载

运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似,注意这里说的重载与我们的函数重载不是一个意思

 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为关键字operator后面接需要重载的运算符符号。

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

所以这里我们可以改写为:

bool operator==(const Date& dt1,const Date& dt2)
{return dt1._year == dt2._year&& dt1._month == dt2._month&& dt1._day == dt2._day;
}
int main()
{Date d1(2024, 4, 22);Date d2(2024, 1, 1);cout << (d1 == d2) << endl;return 0;
}

注意:这里的d1和d2必须用括号括起来,不然会无法运行

这样也是直接成功了,我们可以看一下反义编码去验证一下

 

这里发现调用了operator==,直接进行了函数比较 

但是问题也就来了

这里我们的函数比较是建立在全局变量上的,也就是说我们的函数成员需要变成共有才能够使用函数,既然是共有的成员变量,怎么保证其分装性呢?

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}bool operator==(const Date& dt2){return _year == dt2._year&& _month == dt2._month&& _day == dt2._day;}
private:int _year = -1;int _month = -1;int _day = -1;
};int main()
{Date d1(2024, 4, 22);Date d2(2024, 1, 1);cout << d1.operator==(d2) << endl;cout << (d1 == d2) << endl;return 0;
}

在这里我们将函数分装到类的里面,依旧是利用的运算符重载==

只不过这里我们值传递了一个参数,另一个参数我们利用这里隐藏的this指针去实现代码的构建,从而实现==

细说剖析:

  1. 首先呢,值传递一个参数也就是const Date& dt2,它是右边的比较数的参数,而我们的左边比较数利用的是这里面隐藏的this指针,也及时this指针指向的对象。
  2. 这里呢,我们加入了关键字:const,同时也加入了引用传参。加入这两个是为了避免传递值的修改以及避免空间的开辟浪费和传递的效率。
  3. 最后就是bool值,我们这里的函数返回类型利用的是bool值,如果他们的年月日相同就会返回真(true)也就是1不同就会返回假(false)也就是0。

我们是这调用这个函数 看一看

我们再通过反义编码看一看

我们发现这里都调用了operator==函数 

在上面的讲解之后,相信大家对运算符重载有了一定的了解,他就是允许自定义对象使用运算符它的返回值是根据运算符来决定的比如完成加减操作,我们就返回int类型,判断是否大于小于,就用bool类型

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数(自定义类型参数)
  • 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员的重载函数时其形参看起来比操作数数目少1成员函数的 操作符有一个默认的形参this,限定为第一个形参
  • .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。

 1.2赋值运算符重载

在这里我们知道拷贝赋值有两种,一种是 拷贝构造一种是拷贝赋值

Date d1(2024,4,22);
Date d2(d1);

这样直接进行拷贝构造

那如果运用运算符重载呢?

d2 = d1;

这两者有什么区别呢?

   cout << "Date" << endl;
  1. 拷贝构造函数对象创建时使用,用于初始化新对象赋值运算符重载对象已存在时使用,用于将一个对象的值赋给另一个对象
  2. 其目的是,拷贝构造函数的目的是创建一个新的、状态相同的对象副本。赋值运算符的目的是改变一个已存在对象的状态,使其与另一个对象的状态相同
  3. 拷贝构造函数通常接收一个对同类对象的常引用。赋值运算符重载通常返回对象的引用,并接收一个对同类对象的常引用作为参数

我们在初始化哪里加上一个     

   cout << "Date" << endl;

 运行发现这里只打印了一次Date

也就变向说明了两者的区别

既然运算符重载是将对象的值赋值给另一个对象,我们想一下

可不可以进行连续赋值呢?

比如:a=b=10这种?

在C语言中我们通常是这样的

int a,b;
a = b = 10;

但是在赋值运算符重载中,我们则是需要更新一下自己的方式:返回*this

	Date operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}

在这里我们的返回类型是Date,没有使用引用,而是进行的传值返回

所以我们这里返回的不是*this,而是他的一个拷贝

可以看到返回this指针时调用了他的拷贝

所以为了加快效率我们这次加入引用

Date& operator=(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;return *this;
}
int main()
{Date d1(2024, 4, 22);Date d2(1,1,1);Date d3 = d2 = d1;return 0;
}

这次就没有拷贝 

但是问题来了,如果这里我传参传的是自己呢?如果我给自己赋值会怎么样?

这里是不行的

为什么不行呢?

自赋值在大多数情况下是可以工作的,但是在特定的情况下,如果没有正确处理,它可能会引起错误或意外的行为。考虑自赋值的主要原因是为了确保当对象赋值给自身时,程序仍然能够正确、安全地运行。特别是在类中涉及到动态内存管理时,不正确处理自赋值可能会导致问题。例如,假设一个类内部分配了动态内存,如果在赋值操作中首先释放了这块内存(预备重新分配),而源对象和目标对象实际上是同一个对象,那么这个操作实际上会破坏源对象的状态,导致未定义行为

所以这里我们还需要改进以下代码

	Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;return *this;}}

 我们这里判断条件是地址的比较,如果地址不相同说明不是同一个对象,可以赋值

1.3其它特性 

这里我们看到报错了

然后我们把成员类型设置为公有的,发现还是报错

这是因为:赋值运算符只能重载成类的成员函数不能重载成全局函数

       赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了

       故赋值运算符重载只能是类的成员函数

如果我们不写赋值运算符重载,编译器是否会默认生成呢? 

答案是会的

这里我们把operator=给注释掉后 结果仍旧一样

所以这里与我们拷贝构造等函数性质一致:

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?

答案需要的 

如果我们使用的是栈的话

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc fail");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){_array[_size++] = data;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}

  1. s1对象调用构造函数创建,在构造函数中,默认申请了10个元素的空间,然后存了4个元素1 2 3 4
  2. s2对象调用构造函数创建,在构造函数中,默认申请了10个元素的空间,没有存储元素
  3. 由于Stack没有显式实现赋值运算符重载,编译器会以浅拷贝的方式实现一份默认的赋值运算符重载即只要发现Stack的对象之间相互赋值,就会将一个对象中内容原封不动拷贝到另一个对象中
  4. s2 = s1;当s1给s2赋值时,编译器会将s1中内容原封不动拷贝到s2中,这样会导致两个问题:首先是:s2原来的空间丢失了,存在内存泄漏,
    其次是:s1和s2共享同一份内存空间,最后销毁时会导致同一份内存空间释放两次而引起程序崩溃

这里有点类似于我们之前学习的拷贝构造,如果不分开,也是占用的同一块内存,最后会崩溃报错

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

2.const成员函数

假如我们现在定义一个const对象,想访问它的Print函数,我们发现是调用不了的:

class Date
{
public:Date(int year, int month, int day){//加上这里的打印是为了调试更加清楚cout << "Date" << endl;_year = year;_month = month;_day = day;}bool operator==(const Date& dt2){return _year == dt2._year&& _month == dt2._month&& _day == dt2._day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;return *this;}}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year = -1;int _month = -1;int _day = -1;
};

这里权限进行放大了,接着,我们来介绍const成员函数

原来是const Date*而我的this类型是Date*意味着需要将this指针也改为const Date*所以才有了下面的函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改,内容是只读的

class Date
{
public:Date(int year, int month, int day){//加上这里的打印是为了调试更加清楚cout << "Date" << endl;_year = year;_month = month;_day = day;}bool operator==(const Date& dt2){return _year == dt2._year&& _month == dt2._month&& _day == dt2._day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;return *this;}}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print() const{cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year = -1;int _month = -1;int _day = -1;
};

 我们在Print函数后加上const

这样就可以了

如果没有const修饰的函数呢,我Date类型的对象能否调用const成员函数呢? 

可以的,这里是权限的缩小 

请思考下面的几个问题:

  1. const对象可以调用非const成员函数吗? 不可以,权限放大
  2. 非const对象可以调用const成员函数吗? 可以,权限缩小
  3. const成员函数内可以调用其它的非const成员函数吗? 不可以,权限放大
  4. 非const成员函数内可以调用其它的const成员函数吗?可以,权限缩小

指针和引用才存在权限放大 

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

这个我们主要是了解,不作为重点讲解,后边用到会更新讲解

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year = 1; int _month = 1; int _day = 1; 
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!

这里是默认成员函数,我们删去这两个函数照样可以取地址 

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}//Date* operator&()//{//	return this;//}//const Date* operator&()const//{//	return this;//}
private:int _year = 1;int _month = 1;int _day = 1; 
};
void main()
{Date d1;const Date d2;cout << &d1 << endl;cout << &d2 << endl;
}

这里,我们没有必要深究这个东西究竟有什么用,我们只进行简单的语法了解即可

看到这里,一键三连支持一下呗。

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

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

相关文章

python_django中小学家校互动系统vue_flask家校联系

实现了一个完整的家校互动系统&#xff0c;其中主要有作业信息模块、学校管理员模块、学生学籍模块、学生成绩模块、学科模块、系统新闻模块、系统公告模块、校内新闻模块、校内公告模块、用户表模块、token表模块、关于我们模块、收藏表模块、年级模块、家长模块、教师模块、互…

24V转2.8V2A降压芯片WT6030

24V转2.8V2A降压芯片WT6030 WT6030是一种高效同步整流降压开关模式转换器&#xff0c;集成内部功率MOSFET。该器件在宽输入电源范围内提供3A峰值输出电流&#xff0c;展现出卓越的负载和线路调节性能。其设计仅需要最小数量的外部现成组件&#xff0c;并且采用了节省空间的ESO…

【Linux系统编程】第七弹---权限管理操作(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、修改文件权限的做法(一) 2、有无权限的表现 总结 上一弹我们讲解了Linux权限概念相关的知识&#xff0c;但是我们只知道有…

相机1:如何系相机肩带

开始解锁新领域&#xff0c;多看几个相关视频&#xff0c;大概也就可以掌握一两种系相机肩带的方法&#xff0c;本质就是新知识的学习过程&#xff0c;不可能等着或者期待出来一个完整的教程&#xff0c;一步一步自己去探索&#xff0c;自己去查资料。 目录 总述 第一步&#…

DSP系统的设计过程与选型

DSP的设计步骤分几个阶段&#xff0c;应用系统的设计过程如图所示。 技术指标的确定 器件的选型原则 其他因素的考虑

RTT学习 MQTT

MQTT背景应用 MQTT是机器对机器&#xff08;M2M&#xff09;/物联网&#xff08;IoT&#xff09;连接协议&#xff0c;它是专为受限设备和低带宽、高延迟或不可靠的网络而设计的&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的“轻量级”通讯…

【C语言】每日一题,快速提升(10)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;圣诞树 输入&#xff1a; 1输出&#xff1a; * * * * * **说明&#xff1a; 输入&#xff1a; 2输出&#xff1a; * * * * * * * …

【Elasticsearch】Elasticsearch 从入门到精通(一):基本介绍

《Elasticsearch 从入门到精通》共包含以下 2 2 2 篇文章&#xff1a; Elasticsearch 从入门到精通&#xff08;一&#xff09;&#xff1a;基本介绍Elasticsearch 从入门到精通&#xff08;二&#xff09;&#xff1a;基础使用 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的…

FIR补偿滤波器——matlab的FDA实现

输入采样频率&#xff1a;192KHz 抽取倍数&#xff1a;2 通带截至频率&#xff1a;20KHz 通带衰减&#xff1a;0.1dB 阻带衰减&#xff1a;120dB 在更多选项那里&#xff0c;设置c为0.5&#xff0c;代表抽取倍数为1/c&#xff0c;p设置为4&#xff0c;代表级联阶数。FIR补偿…

3.SpringCloud版本

1.SpringCloud与SpringBoot之间版本对应 2.服务拆分的注意事项 1.不同微服务&#xff0c;不要重复开发相同业务。 2.微服务的数据独立&#xff0c;每个微服务都有自己独立的数据库&#xff0c;不要访问其他微服务的数据库。 3.微服务可以将自己的的业务暴露为接口&#xff…

中电金信:向“新”而行——探索融合架构的项目管理在保险行业的应用

近年来&#xff0c;险企在政策推动、市场牵引、自身发展、新技术应用日趋成熟等内外部因素的驱动下&#xff0c;积极投身到数字化转型的浪潮中。在拜访各类保险客户和合作项目的过程中&#xff0c;我们发现不少险企在数字化转型中或多或少都面临着战略如何落地、技术如何承接和…

国外问卷调查如何做?需要借助海外住宅IP吗?

在数字化时代&#xff0c;国外问卷调查不仅是了解市场需求的重要手段&#xff0c;还成为了一项能够赚取额外收入的方式。随着全球范围内消费者行为的多样化&#xff0c;各类企业和机构越来越需要了解不同地区的用户观点和偏好&#xff0c;以优化产品和服务。 一、国外问卷调查…

【HarmonyOS】Stage 模型 - 基本概念

一、项目结构 如图1所示&#xff1a; 图1 从项目结构来看&#xff0c;这个应用的内部包含了一个子模块叫 entry&#xff0c;模块是应用的基本功能单元&#xff0c;它里面包含源代码、资源、配置文件等。 像这样的模块在应用内部可以创建很多。但模块整体来讲就分成两大类&am…

Ghost Buster Pro for Mac:强大的系统优化工具

Ghost Buster Pro for Mac是一款功能强大的系统优化工具&#xff0c;专为Mac用户设计&#xff0c;旨在提供全方位的系统清理、优化和维护服务。 Ghost Buster Pro for Mac v3.2.5激活版下载 这款软件拥有出色的垃圾清理能力&#xff0c;能够深度扫描并清除Mac上的无效目录、文件…

(C++) 树状数组

目录 一、介绍 二、一维树状数组 2.1 区间长度 2.2 前驱和后继 2.3 查询前缀和 2.4 点更新 三、一维数组的实现 3.1 区间长度函数 3.2 前缀和 3.3 插入/更新 3.4 封装成类 一、介绍 树状数组&#xff08;Binary Indexed Tree&#xff0c;BIT&#xff09;&#xff0c;又称为 …

基于MLP算法实现交通流量预测(Pytorch版)

在海量的城市数据中&#xff0c;交通流量数据无疑是揭示城市运行脉络、洞察出行规律的关键要素之一。实时且精准的交通流量预测不仅能为交通规划者提供科学决策依据&#xff0c;助力提升道路使用效率、缓解交通拥堵&#xff0c;还能为公众出行提供参考&#xff0c;实现个性化导…

【软件测试】认识测试|测试岗位|软件测试和开发的区别|优秀的测试人员需要具备的素质

一、什么是测试 测试在⽣活中处处可⻅ 1.生活中的测试场景 案例⼀&#xff1a;对某款购物软件进⾏测试 *启动测试&#xff1a;点击软件图标&#xff0c;测试软件是否可以正常打开 搜索测试&#xff1a;点击输入框&#xff0c;输入关键词&#xff0c;点击搜索 商品测试&#…

Web3革命:区块链如何重塑互联网

引言 互联网的发展已经深刻地改变了我们的生活方式&#xff0c;而现在&#xff0c;Web3和区块链技术正在为我们提供一个全新的数字世界的视角。本文将带你深入了解Web3的核心概念、技术特性以及它如何正在重塑我们的互联网体验。 从Web1.0到Web3&#xff1a;数字革命的演进 W…

羊大师分析,夏季羊奶的适合人群有哪些?

羊大师分析&#xff0c;夏季羊奶的适合人群有哪些&#xff1f; 夏季羊奶的适合人群相当广泛&#xff0c;主要包括以下几类人群&#xff1a; 生长发育中的孩子&#xff1a;羊奶富含营养&#xff0c;特别是蛋白质和矿物质&#xff0c;对孩子的生长发育有积极的促进作用。 中老年…

谈谈mysql中的各个关键字

1.为什么学习mysql mysql是当今最主流且开放源码的关系型数据库&#xff0c;开发者为瑞典 MySQL AB 公司。目前 MySQL 被广泛地应用在 Internet 上的中小型网站中。由于其体积小、速度快、总体拥有成本低&#xff0c;尤其是开放源码这一特点&#xff0c;许多中小型网站为了降低…