Contents

C++:operator 运算符重载

本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。

资料

重载原理

所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。

实际上,我们已经在不知不觉中使用了运算符重载。例如,+号可以对不同类型(int、float 等)的数据进行加法操作;«既是位移运算符,又可以配合 cout 向控制台输出数据。C++ 本身已经对这些运算符进行了重载。

C++ 也允许程序员自己重载运算符,这给我们带来了很大的便利。

运算符重载

参考:

operator+单参数和双参数的两种写法
C++中operator关键字(重载操作符)
C++ 操作符重载
详解C++编程中的单目运算符重载与双目运算符重载

运算符分类

1、单目运算符: 

单目运算符是指运算所需变量为一个的运算符,又叫一元运算符,其中有:

逻辑非运算符:!、按位取反运算符:~、自增自减运算符:++, –-等。 逻辑非运算符:【!】、按位取反运算符【~】、自增自减运算符【++, –】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*和&】、长度运算符【sizeof】

2、双目运算符:

双目运算符就是对两个变量进行操作,其中有:

初等运算符:下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】 算术运算符: 乘法运算符【*】、除法运算符【/】、取余运算符【%】 、加法运算符【+】、减法运算符【-】 关系运算符: 等于运算符【==】、不等于运算符【!=】 、关系运算符【< > <= >= 】 逻辑运算符: 逻辑与运算符【&&】 、逻辑或运算符【||】、逻辑非运算符【!】 位运算符 :按位与运算符【&】、按位异或运算符【^】 、按位或运算符【|】、左移动运算符【«】、右移动运算符【»】 赋值运算符:赋值运算符【= += -= *= /= %= »= «= &= |= ^=】 逗号运算符:逗号运算符【,】

3、三目运算符:

对三个变量进行操作。

三目运算符对三个变量进行操作,指的是计算机c语言的重要组成部分。条件运算符是唯一有3个操作数的运算符,所以有时又称为三元运算符。一般来说,三目运算符的结合性是右结合的。 对于条件表达式b ? x : y,先计算条件b,然后进行判断。如果b的值为true,计算x的值,运算结果为x的值;否则,计算y的值,运算结果为y的值。一个条件表达式绝不会既计算x,又计算y。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a ? b : c ? d : e将按a ? b : (c ? d : e)执行。[1] <表达式1> ? <表达式2> : <表达式3>; “?”运算符的含义是:先求表达式1的值,如果为真,则执行表达式2,并返回表达式2的结果;如果表达式1的值为假,则执行表达式3,并返回表达式3的结果。 可以理解为条件 ? 结果1 : 结果2 里面的?号是格式要求。也可以理解为是不是条件成立,条件成立为结果1否则为结果2。 注意:在C语言中,结果1 和 结果2的类型必须一致。

1
2
3
4
5
6
int a = 1, b = 2, z, c = 3;

z = a > b ? a : (b > c ? b : c);

cout << "z:" << z << endl;
这样输出的结果是:z:3

运算符重载规则

重载运算符的函数一般格式如下:

1
2
3
4
函数类型 operator 运算符名称 (形参表列)
{
对运算符的重载处理
}

运算符重载为非成员函数的规则:

函数的形参代表依自左至右次序排列的各操作数 重载为非成员函数时 参数个数 = 原操作数个数(后置++、–除外,它们仍然为了区分前置++、–要强行加个int) 至少应该有一个自定义类型的参数(例如"Typ1 operator + (int, double)“非法) 如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元

运算符重载为非成员函数的使用方法:

重载双目运算符U:oprd1 U oprd2 = operator U (oprd1, oprd2) 前置单目运算符U:U oprd = operator U(oprd) 后置单目运算符U:oprd U = operator U(oprd, 0)

前置单目运算符重载:

1
2
3
4
5
6
7
8
9
Time Time::operator++( )    //定义前置自增运算符“++”重载函数
{
    if(++sec>=60)
    {    
        sec-=60;         //满60秒进1分钟
        ++minute;
    }
    return *this;          //返回当前对象值
}

后置单目运算符重载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Time Time::operator++(int)  //定义后置自增运算符“++”重载函数
{
    Time temp(*this);
    sec++;
    if(sec>=60)
    {
        sec-=60;
        ++minute;
    }
    return temp;         //返回的是自加前的对象
}

在代码 **Time operator++(int)**中,注意有个int,在这里int并不是真正的参数,也不代表整数,只是一个用来表示后缀的标志!!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Complex
{
public:
    Complex( double = 0.0, double = 0.0 );
    Complex operator+( const Complex & ) const;
    Complex operator-( const Complex & ) const;
private:
    double real;       // real part
    double imaginary;  // imaginary part
};
Complex Complex::operator+( const Complex &operand2 ) const
{
    return Complex( real + operand2.real, imaginary + operand2.imaginary );
}
Complex Complex::operator-( const Complex &operand2 ) const
{
    return Complex( real - operand2.real, imaginary - operand2.imaginary );
}

c3=c1+c2

最后在C++编译系统中被解释为:

c3=c1.operator+(c2)

操作符重载实现为类成员函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class person{
private:
    int age;
    public:
    person(int a){
       this->age=a;
    }
   inline bool operator == (const person &ps) const;
};

// 实现方式
inline bool person::operator==(const person &ps) const
{
  if (this->age==ps.age)
     return true;
  return false;
}

// 调用方式
#include
using namespace std;
int main()
{
  person p1(10);
  person p2(20);
  if(p1==p2){
    cout<<the age is equal!<<endl;
  }
  return 0;
}

操作符重载实现为非类成员函数(全局函数)

对于全局重载操作符,代表左操作数的参数必须被显式指定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include
#include
using namespace std;
class person
{
public:
int age;
public:
};

bool operator==(person const &p1 ,person const & p2)
//满足要求,做操作数的类型被显示指定
{
if(p1.age==p2.age)
return true;
return false;
}
int main()
{
person rose;
person jack;
rose.age=18;
jack.age=23;
if(rose==jack){
  cout<<"ok"<<endl;
}
return 0;
}

如何决定把一个操作符重载为类成员函数还是全局名字空间的成员

①如果一个重载操作符是类成员,那么只有当与他一起使用的左操作数是该类的对象时,该操作符才会被调用。如果该操作符的左操作数必须是其他的类型,则操作符必须被重载为全局名字空间的成员。
②C++要求赋值=,下标[],调用(), 和成员指向-> 操作符必须被定义为类成员操作符。任何把这些操作符定义为名字空间成员的定义都会被标记为编译时刻错误。
③如果有一个操作数是类类型如string类的情形那么对于对称操作符比如等于操作符最好定义为全局名字空间成员。

重载操作符具有以下限制

(1) 只有C++预定义的操作符集中的操作符才可以被重载;
image/可以被重载的操作符.jpg
(2)对于内置类型的操作符,它的预定义不能被改变,应不能为内置类型重载操作符,如,不能改变int型的操作符+的含义;
(3) 也不能为内置的数据类型定义其它的操作符;
(4) 只能重载类类型或枚举类型的操作符;
(5) 重载操作符不能改变它们的操作符优先级;
(6) 重载操作符不能改变操作数的个数;
(7) 除了对( )操作符外,对其他重载操作符提供缺省实参都是非法的;

重载的意义

在面向对象编程时,常常会建立一个类,例如建立一个矩形类,想判断其中两个对象(我声明的两个矩形)相等,则必须有长相等、宽相等;如果要写一个函数来进行比较,会不如我们常用的“==”运算符直观简单:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class rectangle{
private:
	int length, width;
public:
	rectangle(int l, int w){
            length = l;
	    width = w;
	}
	bool IsSame(const rectangle&);		//比较函数
	bool operator==(const rectangle&);	//重载"=="运算符
};

bool rectangle::IsSame(const rectangle& a){
	if(length==a.length&&width==a.width){
	    return true;
	}
	else return false;
}

bool rectangle::operator==(const rectangle& a){
	if(length==a.length&&width==a.width){
	    return true;
	}
	else return false;
}

int main(){
	rectangle A(5,5);
	rectangle B(5,5);
	if(A.IsSame(B)){
	    cout<<"Same"<<endl;
	}
	if(A==B){				//符合语言习惯 更为直观
	    cout<<"Same~"<<endl;
	}
	return 0;
}

所以,这个使得“==”运算符能被用户定义的类使用的过程就是“重载”。

参考函数例子理解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

class A{
   private :
      int val;
   public :
      A(int i=0):val(i) {}
      A& operator = (const A& B){
         //this->st_val(10);
         return *this;
      }
      int& gt_val(){
         return val;
      }
      void st_val(int value){
         val = value;
      }
}a,b(1);

int main(){
   a = b; // a.operator=(b); 这里 return *this; 是对象A
   cout<<a.gt_val()<<endl;
   cout<<b.gt_val()<<endl;
   return 0;
}
1
2
3
4
A& operator = (const A& B){
   this->st_val(10);
   return *this;
}
  1. A&是一个类型 A 的引用变量,return *this 后,(返回值为A&的)函数会相当于当前变量的别名。
  2. 这里返回了个*this,这代表A=B的执行效果和A一模一样 因为“=”运算符只有一个返回当前*this的作用。甚至没有赋值。