C++ 类和对象 赋值运算符重载

news2024/9/21 4:28:02

前言:

在上文我们知道数据类型分为自定义类型和内置类型,当我想用内置类型比较大小是非常容易的但是在C++中成员变量都是在类(自定义类型)里面的,那我想给类比较大小那该怎么办呢?这时候运算符重载就出现了

一 运算符重载概念:

允许用户为自定义类定义或重新定义运算符的行为,使这些类在使用运算符时表现得像内置数据类型一样,从而提高代码的可读性和简洁性。

1.2 运算符重载与函数重载的区别:

我第一次听到这两个重载都傻傻的分不清楚,以为是一个意思。其实他们的区别可大了

函数重载:

函数重载是指在同一作用域中有多个同名函数,但它们的参数列表(参数的类型和数量)不同。编译器通过参数列表来决定调用哪个函数。函数重载的目的是为了提高代码的可读性和灵活性,使同一操作可以应用于不同类型的参数。

运算符重载:

运算符重载允许我们为用户定义的类型(如类)定义或重新定义特定运算符(如 +、-、*、/ 等)的行为。运算符重载函数的名称为 operator 后跟运算符符号。尽管这些函数的返回类型和参数列表与普通函数类似,但它们的目的是使自定义类型能够使用像内置类型一样的运算符

1.3 运算符特点:

1 定义运算符重载函数:运算符重载是通过定义特殊的成员函数全局函数来实现的

2 运算符重载函数的返回类型:通常是运算符操作后的结果类型。

3 运算符重载函数的参数:根据运算符的类型,参数可以是一个或多个。

4  * :: sizeof ?: . 注意以上5个运算符不能重载

3.1 代码解析:

运算符重载成员函数代码示例:

//成员函数 运算符重载
class Date 
{
public:
	int _x = 5;
	int _y = 4;
	int operator+(const Date& b)
	{
		return this->_x + b._x + this->_y + b._y;
	}

	//错误写法
	//int operator+(const Date& a , const Date& b)
	/*{
		return a.x + b.x + a.y + b.y;
	}*/
};
int main()
{
	//成员函数 运算符重载
	Date d1;
	Date d2;
	int sum = d1 + d2;
	std::cout << "d1 d2总和:" << sum << std::endl;
	return 0;
}

错误写法分析:

因为它是Date类里面的成员函数 又因为成员函数会自带一个隐含的this指针所以成员函数版本的 operator+ 只能有一个显式参数。如果需要两个参数,则应使用全局函数版本的运算符重载。

//全局函数 运算符重载
class Point
{
public:
	//默认构造函数
	Point()
	{
		this->_x = 5;
		this->_y = 15;
	}
//private:
	int _x;
	int _y;
};
bool operator==(const Point& b, const Point& a)
{
	return (b._x == a._x) && (b._y == a._y);
}

int main()
{
	Point f1;
	Point f2;
	int B = f1 == f2;
	std::cout << "1相同 0相否:" << B << std::endl;
	return 0;
}

全局函数的运算符重载是在类外部实现的,不属于任何类,因此没有 this 指针。全局函数可以通过参数访问所有操作数。

假如我把成员函数变成私有的话那在全局函数里面就找不到他们了所以想改变就只能把运算符变量改为成员函数或者用友元函数或getter方法。

二 赋值运算符重载概念:

赋值运算符重载用于定义对象之间的赋值操作,即如何将一个对象的值赋给另一个对象。赋值运算符是 =,它在赋值时被调用。通常我们需要重载赋值运算符来处理深拷贝,以防止浅拷贝带来的问题。

2.1 赋值运算符重载和拷贝构造的区别:

通过赋值运算符重载的概念我们知道它主要的功能是将一个对象的值赋给另一个对象,而这和拷贝构造又非常相似,然而赋值运算符重载与运算符重载只有两字相差却又是不同的内容,这就让我很想知道他们之间的区别到底是什么,接下来让我们一起来解密吧!

概念:

1. 赋值运算符重载

定义对象之间的赋值操作,即如何将一个对象的值赋给另一个对象。

2. 运算符重载:

定义或重新定义自定义类型的运算符行为,使其与内置类型的运算符行为一致。

3. 拷贝构造:

创建一个新的对象,并将其初始化为现有对象的副本。

区别:

拷贝构造函数和赋值运算符重载的主要区别在于它们的使用场景和目的。拷贝构造函数在对象创建时用于初始化新对象,目的是创建一个新的副本。赋值运算符重载在对象已存在时用于赋值操作,目的是修改现有对象的状态,使其与另一个对象的状态相同。拷贝构造函数通常接收一个对同类对象的常引用,而赋值运算符重载通常返回对象的引用,并接收一个对同类对象的常引用作为参数。而运算符重载和赋值运算符重载也真是差了两个字而已,并没有什么区别。

2.1.0 代码解析:

//赋值运算符重载
class Date1
{
public:
	//默认构造函数
	Date1(int year = 2005, int month = 5, int date = 25)
	{
		this->_year = year;
		this->_month = month;
		this->_date = date;
	}

	//拷贝构造函数
	Date1(const Date1& other)
	{
		this->_year = other._year;
		this->_month = other._month;
		this->_date= other._date;
	}

	//赋值运算符重载
	Date1 operator=(const Date1& d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_date = d._date;
		return *this;
	}
	//输出
	void print()
	{
		std::cout << _year << "-" << _month << "-" << _date << std::endl;
	}

private:
	int _year;
	int _month;
	int _date;
};

int main()
{
	//构造函数
	Date1 q1(2024 , 7 , 12);
	Date1 q2(2021 , 6 , 26);

    //拷贝构造
	Date1 q3(q2);

    //赋值运算符重载
	Date1 q4;
	q4 = q1;
    
    //输出
	q1.print();
	q2.print();
	q3.print();
	q4.print();

	return 0;
}

从上面代码可以知道q3是在被创建的时候就直接被调用拷贝构造初始化而q4是先定义好之后在被调用赋值运算符重载初始化的。

那既然赋值运算符重载就是对象之间的赋值那和C语言中的赋值整体意思还是一样的但就是赋值的对象变了,我不知道大家是否还记得在C语言中可以连续赋值,让我们来试试在C++中的赋值运算符重载是否也可以实现呢?

#include <iostream>
class Date 
{
public:
    int _year, _month, _day;

    Date(int year = 2005, int month = 5, int day = 25)
    {
        this->_year = year;
        this->_month = month;
        this->_day = day;
    }

    // 赋值运算符重载
    Date& operator=(const Date& d) 
    {
        // 自赋值检查
        if (this != &d) 
        { 
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
       
        return *this;   // 返回当前对象的引用
    }

    void Print()
    {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }
};

int main() {
    Date d1(2023, 7, 10);
    Date d2;
    Date d3;

    // 链式赋值
    d3 = d2 = d1;

    d1.Print();  
    d2.Print(); 
    d3.Print(); 

    return 0;
}

输出:

虽然输出的结果确实是链式但是又有非常多的疑惑比如为什么要传*this 还有执行顺序是什么……

让我们详细的来解答一下:

首先d2传给隐函数this指针然后d1传给 Date& d那这时d1就是d的引用了,执行到下面就是判断成员变量的地址是否相同(最开始它们之间的地址还是不一样的)跳出循环之后就返回*this即d2的引用返回值类型  Date& (这个我之前一直没看到所以就很困惑)

2.2 默认赋值运算符重载:

编译器生成的默认赋值运算符重载也和默认构造函数中分自定义和内置类型那它与默认构造函数有什么区别,让我们一探究竟吧

内置类型:

概念:编译器生成的默认赋值运算符会直接逐字节拷贝内置类型成员变量的值。

代码演示:

//默认赋值运算符重载(内置类型)
class Date1
{
public:
	//默认构造函数
	Date1(int year = 2005, int month = 5, int date = 25)
	{
		this->_year = year;
		this->_month = month;
		this->_date = date;
	}
	void print()
	{
		std::cout << _year << "-" << _month << "-" << _date << std::endl;
	}

private:
	int _year;
	int _month;
	int _date;
};

int main()
{
    Date1 q1(2024 , 7 , 12);

	Date1 q5;
	q5 = q1;
	q5.print();
	return 0;
}

输出:

就如上图所示,我并没有写赋值运算符重载但是它却给我打印出和q1对象中的成员变量一样的值,所以我们可以得出结论编译器生成的默认赋值运算符会直接逐字节拷贝内置类型成员变量的值。俗称浅拷贝。

自定义类型:

代码演示:


//默认赋值运算符重载(自定义类型)
class Date2
{
public:
   // 赋值运算符重载
    Date2& operator=(const Date2& d)
    {
        if (this != &d) 
        { // 自我赋值检查
            _a = d._a;
        }
        return *this;
    }

private:
    int _a = 10;
};

class Date3
{
public:
    // 默认构造函数
    Date3(int year = 2005, int month = 5, int date = 25)
        {
          this->_year = year;
          this->_month = month;
          this->date = date;
        }
    void print()
    {
        std::cout << _year << "-" << _month << "-" << _date << std::endl;
    }

private:
    int _year;
    int _month;
    int _date;
    Date2 c; // 包含 Date2 类型的成员变量
};

int main()
{
    Date3 q1(2024, 7, 12);

    Date3 q5;
    q5 = q1; // 使用编译器生成的默认赋值运算符

    q5.print();

    return 0;
}

输出:

如图所示编译器生成的默认赋值运算符会调用自定义类型的赋值运算符重载。

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

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

相关文章

ts踩坑!vue3中defineEmits接收父组件向子组件传递方法,以及方法所需传的参数及类型定义!

使用说明 1、在子组件中调用defineEmits并定义要发射给父组件的方法 const emits defineEmits([‘foldchange’]) 2、使用defineEmits会返回一个方法&#xff0c;使用一个变量emits(变量名随意)去接收 3、在子组件要触发的方法中&#xff0c;调用emits并传入发射给父组件的方法…

【C语言初阶】探索编程基础:深入理解分支与循环语句的奥秘

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言入门 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀分支与循环语句 &#x1f4d2;1.…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十三)-更换无人机控制器

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

Kafka:Kafka详解

Kafka 消息中间件 区别于rabbitmq,kafka更适用于量级较大的数据(100w级),主要在大数据领域使用 Kafka介绍 一个分布式流媒体平台,类似于消息队列或企业消息传递系统 Kafak的结构如下 producer:发布消息的对象 topic:Kafak将消息分门别类,每类的消息称为一个主题(Topic) …

《0基础》学习Python——第十一讲__时间函数

一、时间函数是Python中的内置函数和模块&#xff0c;用于处理日期和时间相关的操作。以下是常用的时间函数的种类和用法&#xff1a; 1、time.time()&#xff1a;返回当前时间的时间戳。 时间戳&#xff08;timestamp&#xff09;是一种表示日期和时间的方式&#xff0c;它是一…

高频面试题基本总结回顾4(含笔试高频算法整理)

目录 一、基本面试流程回顾 二、基本高频算法题展示 三、基本面试题总结回顾 &#xff08;一&#xff09;Java高频面试题整理 &#xff08;二&#xff09;JVM相关面试问题整理 &#xff08;三&#xff09;MySQL相关面试问题整理 &#xff08;四&#xff09;Redis相关面试…

使用 SSH 通过 VS Code 连接企业服务器并拉取 Git 仓库代码的指南

文章目录 前言一、SSH 是什么&#xff1f;1.1 SSH 的主要特性和用途1.2 SSH 的工作原理 二、 为什么使用 SSH 而不是 HTTPS三、使用步骤3.1 生成 SSH 密钥3.2 配置 VS Code 远程连接3.3 通过 SSH 克隆 Git 仓库3.4 安装必要的组件 总结 前言 在现代软件开发中&#xff0c;远程…

Sentinel-1 Level 1数据处理的详细算法定义(四)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程,以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下: Sentinel-1 L…

鸿蒙语言基础类库:【@ohos.data.storage (轻量级存储)】

轻量级存储 轻量级存储为应用提供key-value键值型的文件数据处理能力&#xff0c;支持应用对数据进行轻量级存储及查询。数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据类型包括数字型、字符型、布尔型。 说明&#xff1a; 开发前请熟悉鸿蒙开发…

红色文化3D虚拟数字展馆搭建意义深远

在房地产与土地市场的浪潮中&#xff0c;无论是新城规划、乡村振兴&#xff0c;还是商圈建设&#xff0c;借助VR全景制作、虚拟现实和web3d开发技术打造的全链条无缝VR看房新体验。不仅极大提升了带看与成交的转化率&#xff0c;更让购房者足不出户&#xff0c;即可享受身临其境…

前端Vue组件化实践:自定义轮播图组件的探索与应用

在前端开发领域&#xff0c;随着业务逻辑的不断丰富和系统规模的日益扩大&#xff0c;传统的开发方式逐渐暴露出种种弊端。其中&#xff0c;最突出的问题之一便是修改一个小的功能或细节可能导致整个系统的逻辑调整&#xff0c;造成开发效率低下和维护困难。为了应对这些挑战&a…

部署大语言模型并对话

随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;因其强大的语言理解和生成能力而备受关注。OpenWebUI &#xff0c;原名 Ollama WebUI &#xff0c;是一款专为大语言模型&#xff08;LLM&#xff09;设计的先进 Web 交互…

pdf文件怎么转换为jpg图片?这几种转换方法操作起来很简单!

pdf文件怎么转换为jpg图片&#xff1f;在数字化洪流席卷职场的当下&#xff0c;PDF文档虽一度稳坐信息传输与储存的宝座&#xff0c;却逐渐显露出其在效率与便捷性追求中的疲态&#xff0c;随着技术疆界的不断拓宽&#xff0c;我们愈发深刻地意识到&#xff0c;PDF那复杂的格式…

Python array的特点及使用

1、Python array的特点及使用 1.1、python array为什么只能接收指定类型数据 array 模块提供了一种叫做 array 的数据结构&#xff0c;它表示一块连续的内存空间&#xff0c;所有的元素必须是相同的类型。这是因为在内存中&#xff0c;数组元素存储在连续的位置上&#xff0c…

【256 Days】我的创作纪念日

目录 &#x1f33c;01 机缘 &#x1f33c;02 收获 &#x1f33c;03 日常 &#x1f33c;04 成就 &#x1f33c;05 憧憬 最近收到官方来信&#xff0c; 突然发现&#xff0c;不知不觉间&#xff0c;距离发布的第一篇博客已过256天&#xff0c;这期间我经历了春秋招、毕业答辩…

Type-C PD芯片:引领充电技术的新纪元

随着科技的飞速发展&#xff0c;人们对电子设备的依赖日益加深&#xff0c;对充电速度、效率和安全性的要求也越来越高。在这样的背景下&#xff0c;Type-C PD&#xff08;Power Delivery&#xff09;芯片应运而生&#xff0c;以其高效、安全、智能的特点&#xff0c;成为了充电…

gorm多表联合查询 Joins方法 LEFT JOIN , RIGHT JOIN , INNER JOIN, FULL JOIN 使用总结

gorm中多表联合查询&#xff0c;我们可以使用Joins来完成&#xff0c;这个Joins方法很灵活&#xff0c;我们可以非常方便的多多表进行联合查询&#xff0c; 我们先来看看这个方法的官方定义和使用示例&#xff1a; Joins方法定义和使用示例 当然我们这里要说的使用方式是官方示…

网络运输层之(2)UDP协议

网络运输层之(2)UDP协议 Author: Once Day Date: 2024年7月14日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-CSDN…

SQL server 练习题2

课后作业 作业 1&#xff1a;自己查找方法&#xff0c;将 homework_1.xls 文件数据导入到 SQLServer 的 homework 数据库中。数据导入完成后&#xff0c;把表名统一改为&#xff1a;外卖表 如下所示&#xff1a; 作业 2&#xff1a;找出所有在 2020 年 5 月 1 日至 5 月 31 …

离散数学,自反和反自反 ,对称和反对称,传递关系 ,复合关系和逆关系 ,关系的闭包

目录 1.自反和反自反 自反性 反自反性 判断关系是自反或是反自反 2.对称和反对称 对称性 反对称性 判断关系是对称或是反对称 3.传递关系 4.复合关系和逆关系 复合关系 逆关系 关系运算的性质 5.关系的闭包 闭包的性质 1.自反和反自反 自反性 反…