首页 > 编程语言 >9.C++运算符重载

9.C++运算符重载

时间:2022-12-04 19:56:41浏览次数:35  
标签:const 函数 int C++ 运算符 Date 重载

运算符重载

本文包括了对C++类的6个默认成员函数中的赋值运算符重载和取地址和const对象取地址操作符的重载。

运算符是程序中最最常见的操作,例如对于内置类型的赋值我们直接使用=赋值即可,因为这些编译器已经帮我们做好了,但是对象的赋值呢?能直接赋值吗?

概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

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

函数原型:返回值类型 operator操作符(参数列表)

需要注意的几点:

  • 不能通过连接其他符号来创建新的操作符:比如operator@,必须是已有的操作符;

  • 重载操作符必须有一个类类型或者枚举类型的操作数;

  • 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义;

  • 作为类成员的重载函数时,其形参看起来比操作数数目少1,成员函数的操作符有一个默认的形参this,限定为第一个形参;

  • 参数个数与重载的运算符有关;

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

  • 运算符重载作用于左操作数,会把左操作数当做第一个参数;

既然是对自定义类型对象之间的操作符的重载,那么它的参数一定有此类型的对象,并且需要对对象的成员进行操作,这就需要打破封装的限制,那么这个函数应该设置为全局的还是类的成员呢?

有以下几种思路

  1. 函数设为公有,成员变量设为公有(不好);
  2. 函数设为公有另外写一个成员函数区获取成员变量的值(不好);
  3. 将函数设为类的友元函数(可以);
  4. 放入类中,作为成员函数(推荐);
// 全局的operator==
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	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;
}
int main()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 29);
	cout << (d1 == d2) << endl;
	return 0;
}

这样的写法就打破了封装,让类的成员都暴露了出来,这样的损失不太值得。

赋值运算符重载

赋值操作运算符重载特征如下:

  • 参数类型相同;
  • 返回值;
  • 检测是否给自己赋值;
  • 返回*this;
  • 一个类如果没有显式的定义赋值操作符重载,编译器会自动生成一个,完成对象字节序的拷贝(浅拷贝);
  • 赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数。
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Display()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2018, 10, 1);
	// 这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的。
	d1 = d2;
	d1.Display();
	d2.Display();
	return 0;
}

是不是很像自动生成的拷贝构造?那么它也存在一定的问题,对于日期类的对象他能很好的完成赋值操作,可对于指针类型呢?

下面的程序会崩溃

class String
{
public:
	String(const char* str = "songxin")
	{
		cout << "String(const char* str = \"songxin\")" << endl;
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
		_str = nullptr;
	}
private:
	char* _str;
};
int main()
{
	String s1("tanmei");
	String s2;
	s2 = s1;	
	return 0;
}

原因也是因为浅拷贝的关系,导致同一块内存被释放了两次,程序崩溃。

可以不显式定义赋值操作符重载函数的情况

  • 成员变量没有指针;
  • 成员变量有指针,但是指针没有管理内存资源;

注意:赋值操作符重载与拷贝构造不同的地方就是拷贝构造是在对象定义时,而赋值操作符重载是作用于已经存在的对象

const成员

const修饰类的成员函数,有点奇怪,const怎么能修饰函数呢?

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

image-20220522083638398

class Date
{
public:
	Date()//构造函数不写的话创建const的对象会报错。
		:
		_year(1900),
		_month(1),
		_day(1)
	{}
	void Display()
	{
		cout << "Display ()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Display() const
	{
		cout << "Display () const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
	Date d1;
	d1.Display();
	const Date d2;
	d2.Display();
	return 0;
}

const的对象就会调用Display函数会调用哪一个呢?注意到上面代码的第18行的函数被const修饰,那么这个const有什么作用?

实际上这个const修饰的是*this,表明 *this不可被修改,那么const的对象就会调用被const修饰的函数,否则可能会出现下面的问题。

  • const对象可以调用非const成员函数吗?

​ 不可以,权限放大。

  • 非const对象可以调用const成员函数吗?

​ 可以,权限缩小。

  • const成员函数内可以调用其它的非const成员函数吗?

​ 不可以,权限放大。

  • 非const成员函数内可以调用其它的const成员函数吗?

​ 可以,权限缩小。

还有一个值得注意的地方,上面的代码如果我们不显式定义构造函数的话,实例化const的对象时会报错

“d2”: 必须初始化 const 对象

也就是说编译器认为const对象(包括成员)无法被赋值,应该有初始化操作,而默认生成的构造是没有对int有初始化操作的,因此报错;

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

取地址操作符也要重载吗?只有很少的情况会用到,通常直接使用编译器默认生成的就可以。

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

那么什么时候我们会重载呢?

  • 想让别人获取指定的内容的地址;
  • 隐藏对象真实的地址;
class Date
{
public:
	Date* operator&()//隐藏对象真实地址
	{
		return nullptr;
	}
	const int* operator&()const//让用户指定获取成员变量_day的地址
	{
		return  &(_day);
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
	const Date d1;
	Date d2;
	cout << &d1 << endl;//
	cout << &d2 << endl;//
	return 0;
}

输出:

0000005597AFF770
0000000000000000

不过这样的情况确实很少,也没有什么意义。

标签:const,函数,int,C++,运算符,Date,重载
From: https://www.cnblogs.com/ncphoton/p/16950519.html

相关文章

  • 8.C++析构函数
    析构函数既然在创建对象时有构造函数(给成员初始化),那么在销毁对象时应该还有一个清除成员变量数据的操作咯。概念析构函数:与构造函数功能相反,析构函数不是完成对象的销......
  • 13.C++模板初阶
    泛型编程如何实现一个通用的交换函数呢?voidSwap(int&left,int&right){ inttemp=left; left=right; right=temp;}voidSwap(double&left,double&ri......
  • 12.C++内存管理
    在C语言的学习中我们已经接触过内存管理,那么C++与C语言之间又有什么不同和相同的地方呢?C++内存分布intglobalVar=1;staticintstaticGlobalVar=1;voidTest(){......
  • C++图书管理系统(管理员-读者)
    C++图书管理系统(管理员-读者)一、设计一款文字式交互的图书管理系统,要求具备注册登录、浏览图书、借还图书等基本功能;二、要求以外部文件的形式存储书籍信息、馆藏记录、......
  • C++学习---变长参数(stdarg.h)的实现原理
    引用C++中对stdarg.h头文件进行了封装,该头文件实现了函数变长参数,能够在定义函数时不必完全指定参数个数,而编译器能够在代码编译时,拿到所有的参数,并进行相应的处理。stdarg......
  • Java运算符
    +-*/%publicclassTestDemo1{publicstaticvoidmain(String[]args){System.out.println(5/2);System.out.println((double)(5/2));System.out.println(5.......
  • 《“透视”个人大数据》项目开发小记(ANDROID(Java,c++),C#)
       现在进行软件设计开发环境及方式较之以前,已经有了很大的改变,以前要买许多书籍来参考学习,进到书店总能看到有专门的书籍区域,现在一般书店已经基本看不到专门的有关......
  • 【C】关于 sizeof 运算符
    sizeof的实现还是比较巧妙地: //如果对象是数组#definesizeof(ARR)((size_t)(&ARR+1)-(size_t)(&ARR))我们都知道: intx=0;shorty=0;......
  • c++练习267题
    *267题原题传送门:http://oj.tfls.net/p/267题解:#include<bits/stdc++.h>usingnamespacestd;intc,m;intans[30];intsz[1005]={6,2,5,5,4,5,6,3,7,6,8,......
  • C++
    通讯录管理系统1、系统需求通讯录是一个可以记录亲人、好友信息的工具。本教程主要利用C++来实现一个通讯录管理系统系统中需要实现的功能如下:添加联系人:向通讯录中......