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

前言:

在上文我们知道数据类型分为自定义类型和内置类型,当我想用内置类型比较大小是非常容易的但是在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;
}

输出:

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

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

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

相关文章

npm发布的包如何快速在cnpm上使用

npm发布的包如何快速在cnpm上使用 解决方案 前往淘宝npm镜像官网 搜索插件库并点击同步 等待一分钟即可查看最新版本

Nuxt.js 错误侦探:useError 组合函数

title: Nuxt.js 错误侦探&#xff1a;useError 组合函数 date: 2024/7/14 updated: 2024/7/14 author: cmdragon excerpt: 摘要&#xff1a;文章介绍Nuxt.js中的useError组合函数&#xff0c;用于统一处理客户端和服务器端的错误&#xff0c;提供statusCode、statusMessage和…

PostgreSQL修改最大连接数

在使用PostgreSQL 的时候&#xff0c;经常会遇到这样的错误提示&#xff0c; sorry, too many clients already&#xff0c;这是因为默认PostgreSQL最大连接数是 100, 一般情况下&#xff0c;个人使用时足够的&#xff0c;但是在生产环境&#xff0c;这个连接数是远远不够的&am…

内存函数(C语言)

内存函数 以下函数的头文件&#xff1a;string.h 针对内存块进行处理的函数 memcpy 函数原型&#xff1a; void* memcpy(void* destination, const void* source, size_t num);目标空间地址 源空间地址num&#xff0c;被拷贝的字节个数 返回目标空间的起始地…

火星全球彩色影像图介绍(中分辨率相机)

一、数据基本信息 该数据是利用天问一号轨道器中分辨率相机获取的影像经光度校正、几何校正、全球制图等制作而成的全火星地图数据DOM&#xff0c;每个数据包含一个tif数据文件。该影像图分辨率为76米。 任务型号&#xff1a;天问一号 搭载平台&#xff1a;环绕器 数据获…

2.The DispatcherServlet

The DispatcherServlet Spring的Web MVC框架与许多其他Web MVC框架一样&#xff0c;是请求驱动的&#xff0c;围绕一个中央Servlet&#xff08;即DispatcherServlet&#xff09;设计&#xff0c;该Servlet将请求分派给控制器&#xff0c;并提供其他功能以促进Web应用程序的开发…

实现keepalive+Haproxyde 的高可用

需要准备五台实验机 一台客户机&#xff1a;test1 两台&#xff1a;一主一备的实验机&#xff1a;test2 test3 两台真实服务器&#xff1a;nginx1 nginx2 实验 首先在两台实验机上安装Haproxy 安装依赖环境&#xff0c;并将Haproxy的包进行解压处理 yum install -y pcre…

redis redisson(仅供自己参考)

redis 通过setnx实现的分布式锁有问题 如图&#xff1a; 解决的新的工具为&#xff08;闪亮登场&#xff09;&#xff1a;redisson redisson可重入锁的原理 实现语言lua&#xff1a; 加锁实现脚本语言&#xff1a; 释放锁的脚本语言&#xff1a; 加锁的lua -- 首先判断这个锁…

【算法专题】归并排序

1. 排序数组 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; 今天我们使用归并排序来对数组进行排序&#xff0c;实际上&#xff0c;归并排序和快速排序是有一定相似之处的&#xff0c;都运用了分而治之的思想提升了排序效率。快速排序的实现思路是每次排序把区间划分…

【Linux】进程间通信——命名管道和共享内存

目录 命名管道&#xff08;named pipe&#xff09; 命令行中使用 代码中使用 共享内存&#xff08;shared memory&#xff09; shmget ipcs命令 shmctl shmat/shmdt 简单通信 命名管道&#xff08;named pipe&#xff09; 之前我们说了匿名管道&#xff0c;但是匿名管道…

Spring-Spring、IoC、DI、注解开发

1、Spring是什么 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。 Spring整体架构 Spring优点&#xff1a; Spring属于低侵入设计。IOC将对象之间的依赖关系交给Spring,降低组件之间的耦合&#xff0c;实现各个层之间的解耦&#xff0c;让我们更专注于业务…

【python】基于随机森林和决策树的鸢尾花分类

目录 引言 决策树&#xff08;Decision Tree&#xff09; 随机森林&#xff08;Random Forest&#xff09; 数据集 结果 代码实现 引言 随机森林&#xff08;Random Forest&#xff09;和决策树&#xff08;Decision Tree&#xff09;是两种在机器学习中广泛使用的分类和…

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

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

【填坑指南】PHP8报:Unable to load dynamic library ‘zip.so’ 错误

1.原因分析 这种情况多数发生在PHP安装时因为各种原因失败后&#xff0c;残余的库与最后安装的PHP版本不兼容导致的。 2.我的路径 一开始我按照以前摸索出来的安装PHP7.3的成功经验来编译方法安装PHP8.3&#xff0c;发现以前的套路已经失效了。反复重装PHP8.3失败后&#xf…

Sentinel 学习笔记

Sentinel 学习笔记 作者&#xff1a;王珂 邮箱&#xff1a;49186456qq.com 文章目录 Sentinel 学习笔记[TOC] 前言一、基础概念二、Sentinel控制台2.1 安装控制台2.2 簇点链路2.3 请求限流2.4 线程隔离2.5 服务降级2.6 服务熔断 三、Sentinel客户端3.1 原始Jar包客户端3.2 Sp…

python条件

条件语句 if语句 if...else语句 if...elif...else语句 嵌套 is is 是一个身份运算符&#xff0c;用于比较两个对象的身份&#xff0c;即它们在内存中的地址是否相同。这与比较两个对象是否相等的 运算符不同。 运算符比较的是两个对象的值是否相等。 比较对象 比较基本数据…

2024-07-12 Unity AI状态机1 —— 框架介绍

文章目录 1 有限状态机2 状态机实现框架2.1 StateMachine2.2 BaseState2.3 ...State2.4 IAIObject 3 框架类图 本文章参考 B 站唐老狮 2023 年直播内容。点击前往唐老狮 B 站主页。 1 有限状态机 ​ 有限状态机&#xff08;Finite - State Machine&#xff0c;FSM&#xff09…

linux的学习(四):磁盘,进程,定时,软件包的相关命令

简介 关于磁盘管理&#xff0c;进程管理&#xff0c;定时任务&#xff0c;软件包管理的命令的使用 磁盘管理类命令 du du 目录名&#xff1a; 查看文件和目录占用的磁盘空间 参数&#xff1a; -h&#xff1a;可以看到大小的单位&#xff0c;g,mb-a&#xff1a;还可以看到文…

日前光伏功率曲线预测

《利用 2DG&#xff32;A&#xff0d;BiLSTM 模型的日前光伏功率曲线预测方法》 利用2DGRA实现最佳历史相似日数据的获取&#xff0c;根据日功率曲线的波动性将总数据分为3类&#xff08;晴空条件、轻度非晴空条件和重度非晴空条件&#xff09;&#xff0c;根据3种分类&#x…

SpringCloud架构师面试

一、微服务是什么 1、基本概念 微服务是一种架构风格&#xff08;区别于单体架构、垂直架构、分布式架构、SOA架构&#xff09;&#xff0c;应用程序被划分为更小的、流程驱动的服务。 2、微服务的特征 轻量化&#xff1a;将复杂的系统或者服务进行纵向拆分&#xff0c;每个…