草庐IT

C++语法——详解运算符重载

就要 宅在家 2023-11-03 原文

运算符重载是C++的一个重要特性。有了运算符重载,在代码编写时能更好的实现封装。

目录

一.运算符重载介绍

二.运算符重载形式

(一).参数

(二).返回值

(三).应用

三.特殊的运算符重载

(一).默认赋值运算符重载

(二).自增运算符A++与++A

(三).流提取>>与流插入<<

四.不能进行重载的运算符


 

一.运算符重载介绍

运算符重载,就是让原本已经存在的运算符有了新的用法和意义。

比如我们熟知的减号(-),原本是用来进行数字的相减处理。但经过运算符重载后,它可以用来进行其他类型的相减,像时间相减、日期相减、字符相减等等。只要是你能想到的,通过运算符重载基本都能够实现。

对于C++而言,运算符重载一般是作为类的成员函数出现。因为当我们需要运算符重载时,往往是类中一种特殊的类型需要处理或者类本身需要处理。就像我们可能会把时间作为一个类,里面有小时、分钟、秒。如果让时间相减,那么减号的参数类型就变成了时间类。因此,重载最好作为类的成员函数出现,减少在我们调用运算符重载时发生冲突的可能。

二.运算符重载形式

[返回值] operator[运算符] (参数...) { ... };

(一).参数

第一点:

这里我们必须注意的是,重载的参数个数必须与运算符原意的个数一致。比如+号的参数就是左加数和右加数两个。那么当我们重载加号时也要保证有左右两个加数作为参数。

第二点:

重载作为普通函数时:

单目:右操作数是形参;双目:左边形参作为运算符左操作数,右边形参是右操作数。

重载作为类的成员函数时:

如果运算符参数只有一个,那么不需要写参数;如果运算符参数有两个,那么只需要写一个参数。

因为类的成员函数默认有一个this指针。它会直接指向类本身。换句话说,当我们写出运算符重载时,有一个参数就已经被this指针包含了。

单目运算符,this所指向运算符右参数,因为单目运算符的参数一般都在右边。

双目运算符,this指向运算符左参数。

以减号为例,两个时间类a和b相减时。如果是a - b,那么this指针指向a,反之则指向b。在声明函数时,我们只需要写右参数即可。a - b的话只需要写 int operator-(Time b);

当然,单目运算符由于只有一个参数,且该参数被this所指向,那么我们无需声明任何参数即可。

(二).返回值

运算符的返回值类型取决于该重载函数的作用是什么。

比如a + b的作用是得到一个加数,那么返回值就是a+b的值。a += b的作用是让a的值改变,既然是让参数a的值改变,那么就无需返回值。

还有就是如果我们需要运算符支持多次操作那么也需要返回值。比如流插入运算符<<。我们可能需要多次进行插入,像cout << a << b << c;之类就需要返回流ostream本身以便于之后的流插入工作。

(三).应用

下面,我来为大家展示几种运算符重载方式:

加号(双目):

class Time
{
	int _hour;
	int _min;
	int _sec;
public:
	Time(int hour = 0, int min = 0, int sec = 0)
	{
		_hour = hour;
		_min = min;
		_sec = sec;
	}
	void Print()
	{
		cout << _hour << ":" << _min << ":" << _sec << endl;
		
	}
	Time operator+(int min)//加分钟
	{
		 Time t(*this);//因为是+号,规定不能改变左右参数的值,所以使用t来取和用以返回。
		 t._min += min;
		 if (t._min > 59)//检查时间正确性
		 {
			 t._hour += (t._min / 60);
			 if (t._hour > 23)
				 t._hour /= 24;
			 t._min %= 60;
		 }
		 return t;
	}
};
int main()
{
	Time a(10, 30, 30);
	(a + 140).Print();
    return 0;
}

 自增(单目):

class Time
{
	int _hour;
	int _min;
	int _sec;
public:
	Time(int hour = 0, int min = 0, int sec = 0)
	{
		_hour = hour;
		_min = min;
		_sec = sec;
	}
	void Print()
	{
		cout << _hour << ":" << _min << ":" << _sec << endl;
		
	}
	int& operator++()// ++a和a++分别如何我们之后会讲到,这里使用++a
	{
		return _hour += 1;//为方便演示,让小时+1,但不再判断时间正确性
	}
};
int main()
{
	Time a(10, 30, 30);
	++a;
	a.Print();
    return 0;
}

三.特殊的运算符重载

(一).默认赋值运算符重载

即便我们没有手动定义赋值运算符重载,C++也为我们提供了默认函数。

在默认赋值重载中,内置类型按字节拷贝,自定义类型会去调自己的默认赋值重载。这点与默认拷贝构造极为相似,不懂的可以看看这篇文章:详解析构函数、拷贝构造函数

因此,当我们的类中只有int、char、double之类内置类型时,无需再写赋值重载。

但是,如果有指针、malloc、new之类指向地址、手动开辟空间的,一般情况下还是需要手写重载的。

总而言之,默认赋值重载只能进行浅拷贝,是否需要手动去写要根据类的成员类型判断。

class Time
{
	int _hour;
	int _min;
	int _sec;
public:
	Time(int hour = 0, int min = 0, int sec = 0)
	{
		_hour = hour;
		_min = min;
		_sec = sec;
	}
	void Print()
	{
		cout << _hour << ":" << _min << ":" << _sec << endl;
		
	}
	
};
int main()
{
	Time a;
	Time b(20, 10, 50);
	a.Print();
	a = b;//调用默认赋值重载
	a.Print();
    return 0;
}

 值得注意的一点是:

我们需要分清楚赋值重载与拷贝构造的区别。

拷贝构造只能发生在对象被定义时,而赋值则是在对象已经被定义完毕后。

void main()
{
    Time A;
    Time T = A;//拷贝构造
    Time B;
    B = A;//赋值重载
}

(二).自增运算符A++与++A

因为单目运算符重载的this指针指向右边,所以按正常写法我们写出来的是++A。

C++规定,A++的写法是在正常++运算重载基础上,在参数上写一个int类型的参数。

方式如下:

class Time
{
	int _hour;
	int _min;
	int _sec;
public:
	Time(int hour = 0, int min = 0, int sec = 0)
	{
		_hour = hour;
		_min = min;
		_sec = sec;
	}
	void Print()
	{
		cout << _hour << ":" << _min << ":" << _sec << endl;

	}
	//为方便演示,让小时+1,但不再判断时间正确性
	Time& operator++()// ++A
	{
		_hour += 1;
		return (*this);//因为自增直接返回this用引用接收
	}
	Time operator++(int)//A++,参数写int或int i都可以
	{
		Time ret(*this);
		_hour += 1;
		return ret;//需要返回this自增之前的结果,所以用临时变量返回
	}
};
int main()
{
	Time a(10, 30, 30);
    //(++a).Print();
	//a.Print();
	(a++).Print();
	a.Print();
	return 0;
}

 

 

(三).流提取>>与流插入<<

对于流而言,因为是双目运算符,this指针本应该指向左边的类,但左操作数是一个流,又与this的类型冲突。那么就会出现很奇怪的现象:

因为this指针会默认指向类,而我们需要让左参数指向流,右参数指向类。所以重载就不能作为类的成员函数出现了。

这时,就需要用到友元函数friend。友元函数本身是一个普通函数,但是作为类的友元,能够调用类内的成员,包括private。而且参数不用被类限制为第一个必须是this所指的对象本身。

使用时,我们只需要在类内声明有个友元函数即可。

#include"head.h"
class Time
{
	friend ostream& operator<<(ostream& out, Time& t);//友元函数,声明
	friend istream& operator>>(istream& in, Time& t);
	int _hour;
	int _min;
	int _sec;
public:
	Time(int hour = 0, int min = 0, int sec = 0)
	{
		_hour = hour;
		_min = min;
		_sec = sec;
	}
	
};
ostream& operator<<(ostream& out, Time& t)//流插入
{
	out << t._hour << ":" << t._min << ":" << t._sec << endl;
	return out;
}
istream& operator>>(istream& in, Time& t)//流提取
{
	in >> t._hour >> t._min >> t._sec;
	return in;
}
int main()
{
	Time a;
	cin >> a;
	cout << a;
	return 0;
}

 

四.不能进行重载的运算符

在C++中有几种运算符不能进行重载

.*任意字符出现零次或多次
::域作用符
sizeof大小
? :三目运算符
.点运算符

 

 

  • “优良设计创造价值的速度,快于其增加成本的速度。”——托马斯·C.盖勒(Thomas C.Gale)

 如有错误,敬请斧正

 

 

有关C++语法——详解运算符重载的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. ruby - 覆盖相似的方法,更短的语法 - 2

    在Ruby类中,我重写了三个方法,并且在每个方法中,我基本上做同样的事情:classExampleClassdefconfirmation_required?is_allowed&&superenddefpostpone_email_change?is_allowed&&superenddefreconfirmation_required?is_allowed&&superendend有更简洁的语法吗?如何缩短代码? 最佳答案 如何使用别名?classExampleClassdefconfirmation_required?is_a

  5. ruby 语法糖 : dealing with nils - 2

    可能已经问过了,但我找不到它。这里有2个常见的情况(对我来说,在编程Rails时......)用ruby​​编写是令人沮丧的:"astring".match(/abc(.+)abc/)[1]在这种情况下,我得到一个错误,因为字符串不匹配,因此在nil上调用[]运算符。我想找到的是比以下内容更好的替代方法:temp="astring".match(/abc(.+)abc/);temp.nil??nil:temp[1]简而言之,如果不匹配,则简单地返回nil而不会出错第二种情况是这样的:var=something.very.long.and.tedious.to.writevar=some

  6. ruby - Ruby 语法糖有 "rules"吗? - 2

    我正在学习Ruby的基础知识(刚刚开始),我遇到了Hash.[]method.它被引入a=["foo",1,"bar",2]=>["foo",1,"bar",2]Hash[*a]=>{"foo"=>1,"bar"=>2}稍加思索,我发现Hash[*a]等同于Hash.[](*a)或Hash.[]*一个。我的问题是为什么会这样。是什么让您将*a放在方括号内,是否有某种规则可以在何时何地使用“it”?编辑:我的措辞似乎造成了一些困惑。我不是在问数组扩展。我明白了。我的问题基本上是:如果[]是方法名称,为什么可以将参数放在括号内?这看起来几乎——但不完全是——就像说如果你有一个方法Foo.d

  7. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  8. ruby -::在 Ruby 语法中是什么意思? - 2

    这个问题在这里已经有了答案:WhatisRuby'sdouble-colon`::`?(12个答案)关闭8年前。什么是::?@song||=::TwelveDaysSong.new

  9. ruby - ruby 乘法语句中星号中断语法前的空格 - 2

    在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl

  10. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

随机推荐