C++20学习笔记——运算符重载

运算符重载

运算符重载用实例来讲解更加方便,这次我们同时引入自建头文件的编译,比如,我们自定义了一个mySpan类,我们想让mySpan之间可以叠加,以达到现实中两个向量相加的效果:

//MyClasses.h
#pragma once
#include 
#include 
class mySpan
{
public:
	mySpan()
		:x{0},y{0} 
	{}
	mySpan(double X,double Y)
		:x{X},y{Y} 
	{}
	~mySpan()
	{
		std::cout << std::format("mySpan Destoryed\n");
	}
	mySpan operator+(const mySpan& InSpan);     //先在此处声明重载
private:
	double x;
	double y;
};

//MyClasses.cpp
#include "MyClasses.h"
mySpan mySpan::operator+(const mySpan& InSpan)  //在此处定义重载
{
	return mySpan{this->x+InSpan.x,this->y+InSpan.y};
}

//源.cpp
#include "MyClasses.h"
int main()
{
	mySpan span1(1, 1), span2(2, 2);
	mySpan span3 = span1 + span2;
}

现在通过编译后我们便能得到(3, 3)的span3向量了,我们看向函数重载的定义,mySpan mySpan::operator+(const mySpan& InSpan),在 operator前加一个mySpan的限定符是必要的,这使得函数实际上成为mySpan类的成员,以便于我们访问权限为 private的 x和 y。基本的函数重载的结构就是这样子了:

需要返回的类型 operator需要重载的符号(作为类成员重载时此处参数只能有一个)

我们知道mySpan指的是一个向量,我们知道它的表示方法是 (x, y),但很可惜编译器不知道,所以我们需要重载 <<运算符来实现向量的输出,但是这里我们就犯难了:<<的类型在C++中是 std::ostream,但是类内的运算符重载只能传入一个参数。我们一般用友元来实现:

//MyClsses.h
Class mySpan
{
public:
	mySpan()
		:x{0},y{0} 
	{}
	mySpan(double X,double Y)
		:x{X},y{Y} 
	{}

	~mySpan()
	{
		std::cout << std::format("mySpan Destoryed\n");
	}
	mySpan operator+(const mySpan& InSpan);
	friend std::ostream& operator<<(std::ostream& stream, const mySpan& inspan);    //<<的类型是std::ostream,在类内通过添加friend关键词来声明<<的运算符重载是类的友元,让运算符重载获得类内 private元素的权限	
private:
	double x;
	double y;
};

//MyClasses.cpp
std::ostream& operator<<(std::ostream& stream, const mySpan& inspan)    //此处并不需要添加mySpan::限定符,因为<<的重载是全局的
{
	stream << "(" << inspan.x << "," << inspan.y << ")" << std::endl;
	return stream;
}

//源.cpp
#include "MyClasses.h"
int main()
{
	mySpan span1(1, 1), span2(2, 2);
	mySpan span3 = span1 + span2;
	std::cout<<"span3 = " << span3;

输出:

span3 = (3,3)

特殊一些的还有a++和++a(a–和–a同理)运算符的重载,在这里只要是理解了前++和后++的实质其实就很容易写出来:

//MyClasses.cpp
const mySpan mySpan::operator++()     //后++是指先自增再输出
{
	x++;
	y++;
	return *this;
}
const mySpan mySpan::operator++(int)  //前++是指先输出再自增,添加参数int(只能是int)告诉编译器这是前++
{
	mySpan formerSpan = *this;  //保存自增前的量,因为前++输出的是自增前的值
	x++;
	y++;
	return formerSpan;
}

注:如果不在返回值前添加const即默认允许出现类似于span1++++的语法,一般来说是要避免的,所以建议在返回值前添加const



限制和重要指导原则

限制:
·不能发明新的运算符,如?、===或<>。
·不能修改现有运算符的操作数个数、相关性或优先级,也不能改变运算符的操作数的计算顺序。
·一般来说,不能重写内置的运算符,并且重载运算符的签名必须涉及至少一种类类型。我们并不能修改现有运算符操作基本类型或数组类型的方式,例如不能让整数的加法实行乘法操作。


指导原则:运算符重载的主要目的是让使用自己的类的代码更容易编写和阅读,以及降低发生问题的可能性。重载运算符能够使代码更加简洁,但这只是次要考虑因素。简洁但难以理解,甚至有误导性的代码,对任何人都没有帮助。确保代码易于编写和理解,才是真正重要的,因此,应该全力避免让重载运算符与对应的内置运算符具有不同的期望行为。

1.绝不重载逻辑运算符&&或||,如果想为自己的类对象使用运算符重载,则应选择重载&与|,原因是&&与||会发生短路计算,为了避免不必要的bug发生,采用不会进行短路计算的&与|进行重载(同样的原因,因为计算操作数的顺序没有被强制要求,故在C++17之前也不建议重载','(逗号)运算符)。

2.一般约定比较运算符的返回值为bool类型。

3.C++20提供了“太空飞船运算符”<=>,返回值为std::strong_ordering::less、std::strong_ordering::greater、std::strong_ordering::equal以及std::strong_ordering::equivalent(当然还有std::partial::ordering与std::weak_ordering,后面单独出章节介绍)

4.标准库的模块在std::rel_ops名称空间中提供了一组运算符模板,使用T的<和==运算符,为任何类型T定义了<=、>、>=和!=运算符。不过,这种方案有缺陷,不应该再使用。

5.运算符重载大多数时候应该实现为成员函数。只有当不能使用成员函数。或者希望对第一个操作数进行隐式转换时,才能使用非成员函数。


关于C++20中运算符重载的新特性

在C++20之前的版本中,写这类型的运算符重载很难以复用和维护,想象一下我们写了一个10维向量,我们在比较两个向量时只能写十个==比较。好消息是C++20提供了默认的比较运算符重载,它于构造函数以及析构函数相似,但是需要我们主动要求它生成,语法如下:

bool operator==(const mySpan& InSpan) const = default;      //此处是在类内声明

这样我们就可以使用C++20提供的默认==运算符重载了,它的好处在于当我们改变需要比较的参数时不需要手动去更改代码,编译器会主动帮我们维护(C++20是相当聪明哒)。
所以说,如果我们这么写…

auto operator<=>(const mySpan& InSpan) const = default;     //她温,我哭

注:每当默认生成<=>时,编译器也将添加默认生成的==运算符。因此,如果所有比较运算符的默认行为符合要求,就只需要默认生成一个运算符函数:<=>。这太容易了。

你可能感兴趣的:(C++20,c++20,学习,笔记)