首页 > 其他分享 >运算符重载与赋值运算符重载

运算符重载与赋值运算符重载

时间:2023-05-04 21:22:42浏览次数:40  
标签:int month 运算符 year 重载 day 赋值

0. 前言

本章首先提出一个问题以及对应的解决方法, 但是这种解决方法会有缺陷

以此引出运算符重载来改进这个解决方法, 目的是为了更好的理解运算符重载概念以及运算符重载解决了什么问题

之后详细说明运算符重载, 然后再运算符重载的基础上介绍赋值运算符重载

1. 概念引入

如何比较两个日期类对象的大小?

一个比较容易想到的解法是用一个函数去实现, 如下例

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//private:
	int _year;
	int _month;
	int _day;
};

bool Compare(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	return false;
}

int main()
{
	Date d1(2025, 4, 25);
	Date d2(2023, 5, 25);
	cout << Compare(d1, d2) << endl;
}

0表示假, d1 大于 d2

实际上, 这种解法并不好, 因为可读性很差, 看代码的人需要具体去看Compare函数的实现, 才能看懂这段代码Compare(d1, d2)在做什么

如果是这样 d1 < d2, 那么看代码的人可以清楚的明白这是在做比较, d1是否小于d2,这样就增强了可读性

但是, 自定义类型是不能使用运算符的, 因为编译器无法识别, 不能转换成指令

所以C++引入了运算符重载, 为了让自定义类型可以像内置类型一样使用运算符

下面来看一下, 如何使用运算符重载优化解法

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//private:
	int _year;
	int _month;
	int _day;
};

// 重载运算符<
bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	return false;
}

int main()
{
	Date d1(2025, 4, 25);
	Date d2(2023, 5, 25);
	cout << (d1 < d2) << endl;
}

如图, 重载了运算符<, 对象之间就可以使用运算符<进行比较

这样方便使用, 同时又提高了可读性

下面详细说明运算符重载的概念与特性

2. 运算符重载

概念

运算符重载是具有特殊函数名的函数, 具有返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似

运算符重载的定义: 返回值类型 operator操作符(参数列表)

需要注意:

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

 

在概念中提到, 运算符重载是一个函数, 那么在概念引入例子中为什么可以这样(d1 < d2) 调用函数 ?

实际上, (d1 < d2) == operator<(d1, d2), 它们最终会转换为一样的指令 

因为编译器执行(d1 < d2), 会去自动调用operator<(d1, d2), 所以最终的指令都是一样的

 

概念引入的例子还有没有什么问题?

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//private:
	int _year;
	int _month;
	int _day;
};

// 重载运算符<
bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	return false;
}

int main()
{
	Date d1(2025, 4, 25);
	Date d2(2023, 5, 25);
	cout << (d1 < d2) << endl;
}

还有几个问题, 需要一个一个来看

类中的成员变量, 应该设置为私有, 不允许在类外面访问

但是将成员变量设置为私有, 又会出现新的问题, 如上图, 在类外无法访问成员

解决这个问题, 可以把重载运算符函数放在类里面, 作为成员函数, 这样就可以通过this指针访问私有成员变量

但是这样做又会出现新的问题, 如上图

出现问题的原因是, operator<运算符函数只能有两个参数, 而这里有三个 ( x1 + x2 + 隐藏的this指针)

所以需要去掉一个参数, 如下

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 重载运算符<
	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		return false;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2025, 4, 25);
	Date d2(2023, 5, 25);
	cout << (d1 < d2) << endl;
	cout << d1.operator<(d2) << endl;
}

如图, 这样就解决了问题

注意: (d1 < d2) == d1.operator<(d2)  d1调用成员函数operator<

3. 赋值运算符重载

概念引入

有了运算符重载的基础, 直接先看一个使用赋值运算符的例子

#include <iostream>
using namespace std;

class Date
{
public:
	// 构造
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 拷贝构造
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		return false;
	}

	// 赋值运算符重载
	void operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2025, 4, 25);
	Date d2(2023, 5, 25);
	d1 = d2;
	//d1.operator=(d2);
}

如图, d1 = d2, 使用赋值运算符重载将d2赋值给了d1

这里需要注意两个点:

1. d1 = d2, 实际上等于d1.operator=(d2)

2. 一定要搞清楚, 什么是拷贝构造函数, 什么是赋值运算符重载

拷贝构造函数是构造函数的重载形式, 它的作用是使用一个对象去初始化另外一个对象

赋值运算符重载是运算符函数, 它完成的是对象与对象之间的拷贝

它们两个是完全不同的概念, 不能混

 

上面举的例子实际上还存在不足

比如, 不能连续赋值

这是因为d2 = d1的值等于调用赋值运算符函数d2.operator=(d1)的返回值,也就是空

所以需要修改返回值, 这里先将返回值改为对象类型

如图, 连续赋值成功, 但是看调试控制台打印了两个copy,这是为什么?

这是因为函数返回值是一个对象(值), 它会去调用拷贝构造函数完成值拷贝

值返回的代价是很大的, 虽然再这里不会影响效率(因为在这里只拷贝24个字节), 但是如果是一个大对象呢?

这样就会消耗很多的时间与空间, 所以这里最好是引用返回, 因为引用返回不做拷贝, 直接返回, 提高效率

引用返回一定要注意返回对象的生命周期

比如这里, d2 = d1 ---> d2.operator=(d1) ---> 隐藏的this指针等于对象d2的地址 ---> 当生命周期结束d2依然存在, 所以这里可以使用引用返回

下面再进一步介绍赋值运算符重载

概念与特性

赋值运算符重载与构造函数和析构函数一个, 是一个默认的成员函数, 其完成对象与对象之间拷贝的工作

特性如下:

1. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝

2. 内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

3. 赋值运算符只能重载成类的成员函数不能重载成全局函数

 

标签:int,month,运算符,year,重载,day,赋值
From: https://www.cnblogs.com/xumu11291/p/17371173.html

相关文章

  • Java 数组、List初始化赋值
    1数组初始化赋值//第一种初始化赋值方式String[]strs1={"1","2"};//第二种初始化赋值方式String[]strs2=newString[]{"1","2"};2List初始化赋值//第一种初始化赋值方式List<String>strList1=Arrays.asList(newString[]{"1","2"});......
  • profile.ps1 : 无法使用点 '.' 获得此命令来源,因为该命令是在不同语言模式下定 义的。
    使用自定义的PowerShell配置文件(所有用户,所有主机的情况下):打开PowerShell提示:C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1:无法使用点'.'获得此命令来源,因为该命令是在不同语言模式下定义的。要调用此命令而不导入其内容,请忽略'.'运算符。所在位置行:1......
  • 83.赋值运算符
    下表列出了C++支持的赋值运算符:运算符描述实例=简单的赋值运算符,把右边操作数的值赋给左边操作数C=A+B将把A+B的值赋给C+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C+=A相当于C=C+A-=减且赋值运算符,把左边操作......
  • 重写和重载的区别:
    1.重写和重载的区别: a.重载: 1.java中同一个类中,方法名相同,参数列表不同的同名方法 这叫重载。 2.要求俩个方法方法名相同,参数列表不同, 参数列表不同包括:参数的个数不同,参数的类型不同,参数类型位置不同 3.目的:为了让方法接收不同参数时实现不同功能。典型的是多态 b.重写: 1.......
  • Python 中的运算符
    什么是运营商?在编程中,运算符是用于执行数字或逻辑运算的字符。Python支持许多不同类型的运算符。在本课中,我们将学习以下常用运算符:算术运算符比较运算符赋值运算符(用于变量)逻辑运算符成员资格测试运算符(检查给定值内是否存在值)算术运算符Python支持所有用于执行加法......
  • Java-方法重载
    方法重载同一个类中,多个方法的名称相同,但是形参列表不同。方法重载的形式同一个类中,方法名称相同、形参列表不同形参的个数、类型、顺序不同形参的名称无关方法重载的调用流程当程序调用一个重载方法时,编译器会根据参数列表的不同自动匹配最合适的方法,这种机制叫做方法重......
  • C++/PTA 函数重载(数据类型不同)
    题目要求用同一个函数名对n(n<=10)个数据进行从小到大排序,数据类型可以是整数、浮点数,用函数重载实现。输入格式:输入n例如3输入n个整数,例如1089输入n个浮点数例如10.235.167.99输出格式:输出n个整数的升序排列:8910以空格间隔,并以空格结尾换行,输出n个浮点数的升......
  • 第5讲 FPGA运算符详解
    1moduletop(2output[31:0]c3);45localparam[15:0]a=65535;6localparam[15:0]b=25687;789assignc=a*b;10//两个常数相乘,综合后不使用资源,直接综合为一个常数11moduletop(12output[15:0]c1......
  • C语言打印上下金字塔的按位取反运算符的精妙用法
    在打印上下金字塔的通常语句用法应该都是像下面这种#include<stdio.h>intmain(){  intn; do{   for(inti=1;i<n;i++){     for(inta=0;a<n-i;a++){       printf("");     }    for(intj=0;j<2*i-1;j++){     ......
  • java基础-算术运算符(加减乘除取余),隐式转换、强制转换
    一、运算符和表达式的定义运算符:对字面量或者变量进行操作的符号。表达式:用运算符把自变量连接起来,符合java语法的式子就可以称为表达式。例如:inta=10;intb=20;intc=a+b;其中,+,是运算符,并且是算术运算符;a+b是表达式,由于+是算数运算符,所以这个表达式叫算术表达式。二、......