赋值运算符重载和运算符重载

news2024/11/25 4:50:58

1.运算符重载

在C++中,运算符重载是一种强大的特性,它允许我们为已有的运算符赋予新的意义,以便它们能够应用于自定义类型上

这一特性极大地增强了C++的表达能力,使得自定义类型的使用更加直观和自然。例如,如果我们定义了一个复数类,那么通过重载+运算符,我们就可以直接对两个复数对象进行加法运算,而无需编写复杂的函数调用来实现相同的功能。

然而,运算符重载并非没有限制。我们需要遵守C++语言对运算符重载的规则,以避免潜在的问题和混淆。

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this
  5. .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出 现。
  6. 重载后的运算符不能改变其原有的优先级和结合性。
  7. 某些运算符(如=、()、[]、->)只能通过成员函数进行重载,因为这些运算符的左侧操作数必须是类的对象。
  8. 运算符可以重载为成员函数或友元函数。成员函数重载时,左侧操作数隐式地为*this;而友元函数重载则更加灵活,可以访问类的私有和保护成员。

1. 运算符重载的基本概念

定义:

他的定义其实和我们的函数很相似。

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

返回值 +operator+运算符(传的参数)。

例子:

// 全局的operator==
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& d1, const Date& d2)
{
    return d1._year == d2._year
   && d1._month == d2._month
        && d1._day == d2._day;
}
void Test ()
{
    Date d1(2018, 9, 26);
    Date d2(2018, 9, 27);
    cout<<(d1 == d2)<<endl;
}



2. 运算符重载的实现

成员函数重载:

当运算符的左侧操作数是类的对象时,我们通常将该运算符重载为成员函数。例如,重载+运算符以支持两个复数对象的加法运算:

class Complex {
public:
    double real, imag;

    // 构造函数、其他成员函数...

    // 重载+运算符为成员函数
    Complex operator+(const Complex& rhs) const {
        return Complex(real + rhs.real, imag + rhs.imag);
    }
};

 当我们的将假发重载时,我们在进行两个复数进行相加,此时就会自动调用这个函数。

如:Complex s2,s1;   Complex s3=s2+s1;此时s3就是s1和s2这两个复数的和。

如果我们不对加法进行重载的话,我们就不能对自定义类型进行相加。 


友元函数重载:

当运算符需要访问类的私有或保护成员,或者当运算符的左侧操作数不是类的对象时(如输出运算符<<),我们通常将该运算符重载为友元函数。
例如,重载<<运算符以支持复数对象的输出:

class Complex {
    friend std::ostream& operator<<(std::ostream& os, const Complex& c);

public:
    double real, imag;

    // 构造函数、其他成员函数...
};

std::ostream& operator<<(std::ostream& os, const Complex& c) {
    os << c.real << "+" << c.imag << "i";
    return os;
}

3. 常见的运算符重载示例

在C++中,我们可以重载几乎所有的算术运算符、关系运算符、赋值运算符等。以下是一些常见的运算符重载示例:

  1. 算术运算符:+、-、*、/等,用于自定义类型的算术运算。
  2. 关系运算符:==、!=、<、>、<=、>=等,用于自定义类型的比较操作。
  3. 赋值运算符:=,用于自定义类型的赋值操作。注意,当自定义类型包含动态分配的内存时,需要深拷贝以避免悬挂指针等问题。
  4. 自增自减运算符:++、--,用于自定义类型的自增和自减操作。
  5. 下标运算符:[],用于自定义类型的数组或类似数组的操作。
  6. 流插入和提取运算符:<<、>>,用于自定义类型的输入输出操作。
  7. 函数调用运算符:(),允许自定义类型的对象像函数一样被调用。
  8. 成员访问运算符:->,通常与智能指针或类似智能指针的类一起使用,用于访问指针所指向对象的成员。

4. 运算符重载的最佳实践


保持运算符的原有语义:在重载运算符时,应尽可能保持其原有的语义和用法,避免引起混淆。
适度使用:虽然运算符重载非常强大,但过度使用可能会使代码难以阅读和理解。因此,在使用时应权衡利弊,适度使用。
考虑运算符的对称性:对于某些运算符(如+、*等),其操作应该是对称的,即a op b和b op a应该等价。在重载这些运算符时,应注意保持这种对称性。
运算符重载函数应简短高效:运算符重载函数通常会被频繁调用,因此应尽可能简短和高效,以减少不必要的开销。

前置++和后置++重载

对于我们的前置++和后置++重载函数怎么区别也是一个问题。
 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
 C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递。


我们前置++和后置++的区别: 
    后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this+1 而temp是临时对象,因此只能以值的方式返回,不能返回引用

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // 前置++:返回+1之后的结果
 // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
 Date& operator++()
 {
 _day += 1;
 return *this;
 }

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

int main()
{
 Date d;
 Date d1(2022, 1, 13);
 d = d1++;    // d: 2022,1,13   d1:2022,1,14
 d = ++d1;    // d: 2022,1,15   d1:2022,1,15
 return 0;
}

2赋值运算符重载

对于我们的赋值运算符重载,有的并不需要我们自己去进行重载,编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了(比如我们的日期类),但是我们的编译器提供的只会进行浅拷贝,对于一些现需要进行深拷贝的(如:我们的栈),就需要我们去实现我们的赋值运算符重载。

栈:

typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 10)
 {
 _array = (DataType*)malloc(capacity * sizeof(DataType));
 if (nullptr == _array)
 {
 perror("malloc申请空间失败");
 return;
 }
 _size = 0;
 _capacity = capacity;
 }
 void Push(const DataType& data)
 {
 // CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 ~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;
}

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

为什么栈浅拷贝栈不行。 

 

 

我们可以实现日期类来帮助我们熟悉我们的运算符重载。

日期类实现的代码:

 我们的.h文件

#include<iostream>
using namespace std;
#include<assert.h>

class Date
{
	// Ԫ
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	bool CheckDate() const;
	Date(int year = 1900, int month = 1, int day = 1);
	void Print() const;

	// Ĭinline
	int GetMonthDay(int year, int month) const
	{
		assert(month > 0 && month < 13);

		static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };

		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}

		return monthDayArray[month];
	}

	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;

	Date operator+(int day) const;
	Date& operator+=(int day);

	Date operator-(int day) const;
	Date& operator-=(int day);

	// d1++;
	// d1.operator++(0);
	Date operator++(int);

	// ++d1;
	// d1.operator++();
	Date& operator++();

	// d1--;
	// d1.operator--(0);
	Date operator--(int);

	// --d1;
	// d1.operator--();
	Date& operator--();

	// d1 - d2
	int operator-(const Date& d) const;

	//void operator<<(ostream& out);

	Date* operator&()
	{
		//return this;
		//return nullptr;
		return (Date*)0x2673FF40;
	}
	
	const Date* operator&() const
	{
		//return this;
		//return nullptr;
		return (Date*)0x2673FE30;
	}
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

.cpp文件

#include"Date.h"


bool Date::CheckDate() const
{
	if (_month < 1 || _month > 12
		|| _day < 1 || _day > GetMonthDay(_year, _month))
	{
		return false;
	}
	else
	{
		return true;
	}
}

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	if (!CheckDate())
	{
		cout << "非法日期:";
		Print();
	}
}

// void Date::Print(const Date* const this)
void Date::Print() const
{
	//++_year;
	cout << _year << "/" << _month << "/" << _day << endl;
}

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

	return false;
}

// d1 <= d2
bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}

bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}

bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}

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

bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

// d1 += 100
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= (-day);
	}

	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

// d1 + 100
Date Date::operator+(int day) const
{
	Date tmp = *this;
	tmp += day;

	return tmp;
}


Date Date::operator-(int day) const
{
	Date tmp = *this;
	tmp -= day;

	return tmp;
}

// d1 -= 100
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}

	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

// d1++;
// d1.operator++(0);
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

// ++d1;
// d1.operator++();
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

// d1 - d2
int Date::operator-(const Date& d) const
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n * flag;
}


ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

// 11:45
istream& operator>>(istream& in, Date& d)
{
	while (1)
	{
		cout << "请依次输入年月日:>";
		in >> d._year >> d._month >> d._day;

		if (!d.CheckDate())
		{
			cout << "输入日期非法:";
			d.Print();
			cout << "请重新输入!!!" << endl;
		}
		else
		{
			break;
		}
	}

	return in;
}

 

3. const成员函数

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

形如: 成员函数+const

例子:

class Date{
public;
    void Display()
 {
   cout<<" "<<_year<<" "<<_month<<" "<<_day<<endl;
 }
private:
 int _year; // 年
 int _month; // 月
 int _day; // 日

};

 相关问题:

在C++中,关于const对象、非const对象、const成员函数和非const成员函数之间的调用关系,有一些明确的规则:

const对象能调用非const成员函数吗?

不可以。因为非const成员函数可以修改对象的状态(即成员变量的值),而const对象在定义时就被承诺不会修改其任何成员变量。因此,从逻辑上讲,const对象不应该能够调用可能会修改其状态的非const成员函数。

非const对象能调用const成员函数吗?

可以。const成员函数保证不会修改对象的任何成员变量,因此非const对象可以安全地调用它们。非const对象没有关于其状态不被修改的承诺,因此它可以调用任何成员函数,包括const成员函数。

const成员函数内可以调用非const成员函数吗?

不可以(直接地)。由于const成员函数承诺不会修改对象的状态,因此它不能直接调用可能修改对象状态的非const成员函数。如果确实需要在const成员函数内部执行某些需要修改对象状态的操作,通常的做法是将这些操作封装在另一个函数中,并通过某种机制(如使用指向非const对象的指针或引用)来绕过const限制,但这通常不是推荐的做法,因为它违背了const成员函数的设计初衷。

非const成员函数内可以调用其他const成员函数吗?

可以。非const成员函数可以调用const成员函数,因为const成员函数不会修改对象的状态,这符合非const成员函数可能进行的任何操作。

所以,const成员函数和非const成员函数之间的调用关系主要基于它们对对象状态修改的承诺。const成员函数保证不会修改对象的状态,因此它们可以安全地被任何类型的对象(包括const对象)调用,但不能直接调用可能修改对象状态的非const成员函数。反之,非const成员函数可以调用const成员函数,因为它们没有关于不修改对象状态的限制。

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

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

关于我们的&操作符的重载,是一个很有意思的整蛊你朋友的函数,你可以重载我们的&操作符,当我们的人使用我们的&操作符就是不会成功的使用默认的,而会使用我们自己重载的,这个时候你就可以让取不到我们的地址,我们就而已给他们一个另外的错误的地址。

class Date
{ 
public :
 Date* operator&()
 {
 return this ;
 
 }
 
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};

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

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

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

相关文章

【区块链+绿色低碳】包头林草市域碳中和体系建设项目 | FISCO BCOS应用案例

在双碳体系建设背景下&#xff0c;政府、企业都在积极探索碳中和价值实现路径。但是在林业碳汇场景中&#xff0c;存在着林权认证、 身份授权、多方机构协作、数据交换等流程&#xff0c;在这些复杂的业务协作中存在一些风险&#xff0c;如&#xff1a;身份信息泄漏、数据造假、…

BSV区块链技术现实应用原理解析

BSV区块链以其卓越的可扩展性、坚如磐石的安全性、极低的交易成本等特性&#xff0c;成为满足企业当下需求并为企业未来成功奠基铺路的理想技术。 BSV协会近期发布了一个题为《驾驭数字化转型&#xff1a;在自动化世界中建立信任——区块链在数据保护和交易优化中的角色》的报…

Java代码基础算法练习-竞猜卡片值-2024.07.22

任务描述&#xff1a; 小米和小王玩竞猜游戏&#xff1a;准备7张卡片包含数字2、3、4、5、6、7、8&#xff0c;从中抽出2张&#xff08;有 顺序之分&#xff0c;抽2、3跟抽3、2是两种情况&#xff09;&#xff0c;猜2张卡片的和&#xff0c;如果是奇数&#xff0c;则猜对。小米…

mmpretrain报错解决记录-socket.gaierror: [Errno -2] Name or service not known

在使用Beit模型时出现 mmengine - INFO - load model from: https://download.openmmlab.com/mmselfsup/1.x/target_generator_ckpt/dalle_encoder.pth 07/19 11:27:30 - mmengine - INFO - Loads checkpoint by http backend from path: https://download.openmmlab.com/mmsel…

unity2022 il2cpp 源码编译

新建一个XCODE静态库工程 从unity安装目录中找到il2cpp源码 Editor\Data\il2cpp\ 改名 il2cpp/libil2cpp -> il2cpp/il2cpp 加入工程中 ->工程根目录 extends/zlib libil2cpp/ buildSettings 相关设置 IOS Deployment Target ios 12.0 Header Search Paths $(in…

总结——TI_音频信号分析仪

一、简介 设备&#xff1a;MSPM0G3507 库&#xff1a;CMSIS-DSP TI 数据分析&#xff1a;FFT 软件&#xff1a;CCS CLion MATLAB 目的&#xff1a;对音频信号进行采样&#xff08;滤波偏置处理&#xff09;&#xff0c;通过FFT获取信号的频率成分&am…

7.23模拟赛总结 [数据结构优化dp] + [神奇建图]

目录 复盘题解T2T4 复盘 浅复盘下吧… 7:40 开题 看 T1 &#xff0c;起初以为和以前某道题有点像&#xff0c;子序列划分&#xff0c;注意到状态数很少&#xff0c;搜出来所有状态然后 dp&#xff0c;然后发现这个 T1 和那个毛关系没有 浏览了一下&#xff0c;感觉 T2 题面…

前端(1)HTML

1、标签 创建1.html文件&#xff0c;浏览器输入E:/frontheima/1.html&#xff0c;可以访问页面 页面展示 在VSCODE安装IDEA的快捷键&#xff0c;比如ctld复制一行、ctrlx剪切 <p id"p1" title"标题1">Hello,world!</p> <p id"p2"…

Java | Leetcode Java题解之第268题丢失的数字

题目&#xff1a; 题解&#xff1a; class Solution {public int missingNumber(int[] nums) {int n nums.length;int total n * (n 1) / 2;int arrSum 0;for (int i 0; i < n; i) {arrSum nums[i];}return total - arrSum;} }

[排序]hoare快速排序

今天我们继续来讲排序部分&#xff0c;顾名思义&#xff0c;快速排序是一种特别高效的排序方法&#xff0c;在C语言中qsort函数&#xff0c;底层便是用快排所实现的&#xff0c;快排适用于各个项目中&#xff0c;特别的实用&#xff0c;下面我们就由浅入深的全面刨析快速排序。…

PHP接入consul,注册服务和发现服务【学习笔记】

PHP接入consul,注册服务和发现服务 consul安装 链接: consul安装 启动consul C:\Users\14684>consul agent -dev安装TP5 composer create-project topthink/think5.0.* tp5_pro --prefer-dist配置consul 创建tp5_pro/application/service/Consul.php <?php /*****…

环境变量配置文件中两种路径添加方式

环境变量配置文件中两种路径添加方式 文章目录 环境变量配置文件中两种路径添加方式代码示例区别和作用 代码示例 export HBASE_HOME/opt/software/hbase-2.3.5 export PATH$PATH:$HBASE_HOME/binexport SPARK_HOME/opt/software/spark-3.1.2 export PATH$SPARK_HOME/bin:$PAT…

【MySQL是怎样运行的 | 第一篇】Explain执行计划上

文章目录 1.查询优化-Explain语句详解上1.1前言1.2执行计划输出各列详解1.2.1 table1.2.2 id1.2.3 select_type1.2.4 partitions1.2.5 type1.2.6 possible_keys和key1.2.7 key_len1.2.8 ref1.2.9 rows 世纪晚霞 1.查询优化-Explain语句详解上 1.1前言 一条查询语句在经过 MyS…

在 CentOS 7 上安装 Docker 并安装和部署 .NET Core 3.1

1. 安装 Docker 步骤 1.1&#xff1a;更新包索引并安装依赖包 先安装yum的扩展&#xff0c;yum-utils提供了一些额外的工具&#xff0c;这些工具可以执行比基本yum命令更复杂的任务 sudo yum install -y yum-utils sudo yum update -y #更新系统上已安装的所有软件包到最新…

视频分帧【截取图片】(YOLO目标检测【生成数据集】)

高效率制作数据集【按这个流程走&#xff0c;速度很顶】 本次制作&#xff0c;1059张图片【马路上流动车辆】 几乎就是全自动了&#xff0c;只要视频拍得好&#xff0c;YOLO辅助制作数据集就效率极高 视频中的图片抽取&#xff1a; 【由于视频内存过大&#xff0c;遇到报错执行…

2024导游资格考试,这些材料提前准备✅

2024年导游考试报名本月开始&#xff01; &#x1f499;大家提前准备好报名材料 1、个人近期白底1寸证件照。 2、身份证照片 3、学历照片 4、健康证明或健康承诺书 5、其他需要上传的材料 &#x1f499;照片文件不通过原因汇总&#xff0c;记得避开这些坑&#xff01; &#x1…

网络驱动移植(RTL8189)

1、把驱动放到内核文件夹中&#xff08;linux/drivers/net/wireless&#xff09;&#xff0c;对应的驱动可以在网上下载 2、修改该目录下的Kconfig和Makefile文件 3、配置内核&#xff08;make menuconfig&#xff09; 配置支持IEEE 802.11&#xff0c;选中8189模块&#xff0…

程序员 被辞退后如何赚钱

为啥现在大厂大量裁员&#xff1f;35以上的程序员为啥不被认可&#xff0c;很难找工作&#xff1f; 技术更新换代迅速&#xff1a; 技术领域发展极快&#xff0c;新的编程语言、框架和技术不断涌现。如果大龄程序员未能及时学习新技术和跟上行业变化&#xff0c;可能会被新一代…

SAP第二季度财报和进一步裁员计划

7月22日公布了截至 2024 年 6 月 30 日的第二季度财务报告。以下位总体指标 当前云计算在手订单达 148 亿欧元&#xff0c;按名义货币和固定汇率计算均增长 28%云收入增长 25%&#xff0c;其中云 ERP 套件收入增长 33%&#xff0c;均按名义货币和固定汇率计算总收入增长 10%&a…

【数据结构初阶】复杂度

目录 一、时间复杂度 1、时间复杂度的概念 2、大O的渐进表示法 3、常见的时间复杂度计算举例 二、空间复杂度 1、空间复杂度的概念 2、常见的空间复杂度计算举例 三、常见复杂度对比 正文开始—— 前言 一个算法&#xff0c;并非越简洁越好&#xff0c;那该如何衡量一个算法…