【c++】类和对象(五)赋值运算符重载

这篇具有很好参考价值的文章主要介绍了【c++】类和对象(五)赋值运算符重载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

🔥个人主页Quitecoder

🔥专栏c++笔记仓

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

朋友们大家好,本篇文章带大家认识赋值运算符重载,const成员,取地址及const取地址操作符重载等内容

1.赋值运算符重载

1.1运算符重载

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

什么意思呢,我们来讲解:首先我们定义日期类Date,并实例化两个对象:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2018, 9, 26);
	Date d2(2024, 3, 29);
	return 0;
}

我们如何判断两个年份相等呢?

如果是常规方法,我们会写一个比较函数,来判断是否相同:

bool issame(const Date& d1, const Date& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}
int main()
{
	Date d1(2018, 9, 26);
	Date d2(2024, 3, 29);
	cout << issame(d1, d2) << endl;
	return 0;
}

那如果我们想直接通过用d1==d2来判断是否相同呢?这里就用到了操作符重载

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

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

bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}
int main()
{
	Date d1(2018, 9, 26);
	Date d2(2024, 3, 29);
	cout << (d1==d2) << endl;
	return 0;
}

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
我们发现,直接进行判断时,调用了比较函数

但是这里是全局的定义的operator==,这里会发现运算符重载成全局的就需要成员变量是公有的,即我的成员不能是private私有的,那么封装性如何保证?

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

这部分是Date类中==运算符的重载。这个重载让你可以使用==来比较两个Date对象是否相等,即它们的年、月、日是否都相同

关键点讲解

  • 参数operator==函数接受一个类型为const Date&的参数d2,它是比较操作的右侧操作数。左侧操作数是调用这个函数的对象,this指针指向的对象
  • const关键字:参数使用const修饰符和引用传递来保证效率和避免不必要的拷贝,同时确保不会修改传入的对象
  • 函数体:函数体中,通过比较两个Date对象的年、月、日字段来决定这两个对象是否相等。如果所有字段都相等,则返回true;否则,返回false

我们接着调用这个函数:

int main()
{
	Date d1(2018, 9, 26);
	Date d2(2024, 3, 29);
	cout << d1.operator==(d2) << endl;
	cout << (d1==d2) << endl;
	return 0;
}

注意
注意这里的顺序,d1在前与在后是不同的,如果我们写一个小于函数的运算符重载,顺序不同,意思刚好相反

我们有两种方式进行调用,这两种方式是相同的:
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

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

1.1.1特性:

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

1.2赋值运算符重载

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
我们知道,拷贝赋值有两种,拷贝构造和赋值重载,我们看拷贝构造:

Date d1(2018, 9, 26);
Date d2(d1);

那如果我们用赋值运算符重载呢?可以写成下面的形式:

d2=d1;

关键区别

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

我们接下来讲解赋值运算符重载的具体实现来体现上面的特点:

能不能直接这么写呢?:

void operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

这个在单个赋值是可以的,那如果,我想像c语言一样同时实现多个变量的连续赋值的场景呢?

int b;
int c;
b=c=10;

那我们这个函数就无法满足要求了,我们该如何修改呢?

我们不妨探讨连续赋值的本质:

b=c=10;

这里执行步骤:

  • 10赋值给c,c=10这个表达式返回值为左操作数c
  • c再作为b=c的有操作数给b赋值,返回值为左操作数b

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

所以,我们的自定义类型也要符合这里的行为

所以,我们需要对函数进行修改:

第一次修改

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

返回左操作数,返回*this

我们这里用的是传值返回,意味着这里返回的不是*this,返回的是*this的拷贝,则需要调用拷贝构造函数:
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
所以我们需要再次修改:

第二次修改:

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

我们返回引用

还有一个问题,如果自身给自身赋值呢?

d1=d1;

为什么自赋值不行?

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

我们还需要再次修改一次:

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

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

1.3 赋值运算符的其他性质

赋值运算符只能重载成类的成员函数不能重载成全局函数
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

我们首先得把成员类型设置为公有的,发现还是报错,
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

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

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

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
结果是会生成的

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

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

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

答案是需要的,如遇到下面的动态内存管理

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;
}

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

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

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

1.4前置++和后置++重载

在C++中,前置++和后置++运算符都可以被重载,以提供用户定义类型(比如类)的自增功能。它们之间的主要区别在于参数和返回值,这影响了它们的使用和效率

前置++

前置++直接对对象进行自增操作,并返回自增后的对象引用。这意味着它在自增后立即返回对象的状态,使得操作可以立即反映在对象上

Date& operator++()
 {
 _day += 1;
 return *this;
 }

后置++

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

注意:后置++是先使用后+1因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1

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

temp是临时对象,因此只能以值的方式返回,不能返回引用

2.const成员函数

假如我们现在定义一个const对象,想访问它的Print函数,我们发现是调用不了的:
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

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

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

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

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

class Date
{
public:
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout << "Print()" << endl;
 cout << "year:" << _year << endl;
 cout << "month:" << _month << endl;
 cout << "day:" << _day << endl << endl;
 }
 void Print() const
 {
 cout << "Print()const" << endl;
 cout << "year:" << _year << endl;
 cout << "month:" << _month << endl;
 cout << "day:" << _day << endl << endl;
 }
private:
 int _year; // 年
 int _month; // 月
 int _day; // 日
};
void Test()
{
 Date d1(2022,1,13);
 d1.Print();
 const Date d2(2022,1,13);
 d2.Print();
}

我们查看结果:
【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++

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

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; 
	int _month; 
	int _day; 
};
void main()
{
	Date d1(2022, 1, 13);
	d1.Print();
}

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
可以的,这里是权限的缩小

请思考下面的几个问题:

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

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

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

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{

		_year = year;
		_month = month;
		_day = day;
	}
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

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

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
这里是默认成员函数,我们删去这两个函数照样可以取地址

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{

		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

【c++】类和对象(五)赋值运算符重载,c++笔记仓,c++
所以,我们没有必要深究这个东西究竟有什么用,我们只进行简单的语法了解即可

当然,如果你想让它普通对象定义后只能返回空值,你可以这么写:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{

		_year = year;
		_month = month;
		_day = day;
	}
	Date* operator&()
	{
		return nullptr;
	}
private:
	int _year; 
	int _month; 
	int _day; 
};

日常并没有这种需要

本节内容到此结束!!!感谢大家阅读!!文章来源地址https://www.toymoban.com/news/detail-846296.html

到了这里,关于【c++】类和对象(五)赋值运算符重载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】四、类和对象(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】三、类和对象 (面向过程、class类、类的访问限定符和封装、类的实例化、类对象模

    2024年02月05日
    浏览(40)
  • C++——类和对象之运算符重载

    本章思维导图: 注:本章思维导图对应的 xmind 文件和 .png 文件都已同步导入至”资源“ 我们都知道, 对于内置类型我们是可以直接用运算符直接对其进行操作的,但是对于自定义类型,这种做法是不被允许的 。 例如对于 Date 类: 因此, 为了解决自定义类型不能使用操作

    2024年02月05日
    浏览(37)
  • C++——类和对象3|日期类型|Cout运算符重载|Cin运算符重载|const成员|

    目录 日期类型  Date.h  Date.cpp  Test.cpp  实现Cout运算符重载  实现Cin运算符重载  根据日期算星期  修改后完整代码   Date.h  Date.cpp  const成员  取地址及const取地址操作符重载 习题  计算日期到天数转换     一个类到底可以重载哪些运算符,要看哪些运算符对这个类型有

    2023年04月13日
    浏览(48)
  • c++类与对象(二)——赋值运算符重载与取地址操作符重载

    前言: 本章将通过 日期类 的实现,深入学习 运算符重载 的实现方法。本章将完成6个默认成员函数中剩余3个—— 赋值运算符重载 与 取地址操作符重载 的学习。 C++ 为了增强代码的可读性引入了 运算符重载 ,运算符重载是具有 特殊函数名 的函数,也具有其返回值类型,

    2024年02月03日
    浏览(32)
  • 【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C++   🔝🔝 本章重点: 本篇文章将详细讲解拷贝构造函数 和运算符重载,并介绍const成员的概念 拷贝构造函数和运算符重载 是类和对象中六大默认成员函数

    2024年02月14日
    浏览(30)
  • [C++ ]:5.类和对象中(运算符重载补充)+ 类和对象下(初始化列表)

    我们知道进行运算符重载这个函数的参数的左右类型是非常重要的,我们尝试在类中去定义这个流插入重载! 1. 考虑到隐含的参数指针: 2.进行优化! 我们观察上面的代码发现可以实现在类中进行流插入运算符的一个重载但是我们需要考虑隐含参数的位置所以我们进行传参

    2024年02月06日
    浏览(33)
  • 【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】四、类和对象 (构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)-CSD

    2024年02月05日
    浏览(32)
  • 【C++】类和对象(中)之拷贝构造与运算符、操作符重载

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》 🌝 每一个不曾起舞的日子,都是对生命的辜负 我们继续学习默认成员函数,本篇文章博主带来的是拷贝构造函数与运算符、操作符重载的讲解,并且还有

    2024年02月05日
    浏览(38)
  • 【C++初阶】类与对象:6大默认成员函数------拷贝构造和赋值运算符重载

      拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰) ,在用已存在的类类型对象创建新对象时由编译器自动调用。 1. 拷贝构造函数是 构造函数的一个重载形式 ; 2. 拷贝构造函数的 参数只有一个且必须是类类型对象的引用 ,使用传值方式编

    2024年02月03日
    浏览(33)
  • 【C++深入浅出】类和对象中篇(六种默认成员函数、运算符重载)

    目录 一. 前言  二. 默认成员函数 三. 构造函数 3.1 概念 3.2 特性 四. 析构函数 4.1 概念 4.2 特性 五. 拷贝构造函数 5.1 概念 5.2 特性 六. 运算符重载 6.1 引入 6.2 概念 6.3 注意事项 6.4 重载示例 6.5 赋值运算符重载 6.6 前置++和后置++运算符重载 七. const成员函数 7.1 问题引入 7.2 定义

    2024年02月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包