C/C++对bool operator < (const p &a)const的认识,运算符重载详解(杂谈)

下面来进行这段代码的分析:

struct node {  //定义一个结构体node(节点)

int x;

int y;

int len;   //node中有3个成员变量x,y,len

bool operator <(const node &a)const {//重载<操作符。可以对两个node使用<操作符进行比较

return len<a.len;

}

};

括号中的const表示参数a对象不会被修改,最后的const表明调用函数对象不会被修改!

想必看到这里对重载运算符算是有一丁点的了解吧,下面看些理论文字,更加清楚的了解关系!

重载运算符的介绍

C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。 

  运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下:

<返回类型说明符> operator <运算符符号>(<参数表>) {      <函数体> }

 运算符重载时要遵循以下规则:

(1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载。 (2) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。 (3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。 (4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。 (5) 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。 (6) 运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。

  运算符函数重载一般有两种形式:重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)   

成员函数运算符

 运算符重载为类的成员函数的一般格式为:     <函数类型> operator <运算符>(<参数表>)     {      <函数体>     }

  当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:

(1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。 (2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。 (3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。     调用成员函数运算符的格式如下:     <对象名>.operator <运算符>(<参数>)     它等价于     <对象名><运算符><参数>     例如:a+b等价于a.operator +(b)。一般情况下,我们采用运算符的习惯表达方式。

友元函数运算符

 运算符重载为类的友元函数的一般格式为:     friend <函数类型> operator <运算符>(<参数表>)     {      <函数体>     }

  当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。

 调用友元函数运算符的格式如下:     operator <运算符>(<参数1>,<参数2>)     它等价于     <参数1><运算符><参数2>     例如:a+b等价于operator +(a,b)。

两种重载形式的比较

  在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:

(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。 (2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。 (3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。 (4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。 (5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。 (6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一 个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。 (7) 当需要重载运算符具有可交换性时,选择重载为友元函数。

下面具体讲讲重载运算符的一些具体用法吧!

1.一般运算符重载

在进行对象之间的运算时,程序会调用与运算符相对应的函数进行处理,所以运算符重载有两种方式:成员函数和友元函数。成员函数的形式比较简单,就是在类里面定义了一个与操作符相关的函数。友元函数因为没有this指针,所以形参会多一个。

 1     class A
 2     {
 3     public:
 4      A(int d):data(d){}
 5      A operator+(A&);//成员函数
 6      A operator-(A&);
 7      A operator*(A&);
 8      A operator/(A&);
 9      A operator%(A&);
10      friend A operator+(A&,A&);//友元函数
11      friend A operator-(A&,A&);
12      friend A operator*(A&,A&);
13      friend A operator/(A&,A&);
14      friend A operator%(A&,A&);
15     private:
16      int data;
17     };
18     //成员函数的形式
19     A A::operator+(A &a)
20     {
21      return A(data+a.data);
22     }
23     A A::operator-(A &a)
24     {
25      return A(data-a.data);
26     }
27     A A::operator*(A &a)
28     {
29      return A(data*a.data);
30     }
31     A A::operator/(A &a)
32     {
33      return A(data/a.data);
34     }
35     A A::operator%(A &a)
36     {
37      return A(data%a.data);
38     }
39     //友元函数的形式
40     A operator+(A &a1,A &a2)
41     {
42      return A(a1.data+a2.data);
43     }
44     A operator-(A &a1,A &a2)
45     {
46      return A(a1.data-a2.data);
47     }
48     A operator*(A &a1,A &a2)
49     {
50      return A(a1.data*a2.data);
51     }
52     A operator/(A &a1,A &a2)
53     {
54      return A(a1.data/a2.data);
55     }
56     A operator%(A &a1,A &a2)
57     {
58      return A(a1.data%a2.data);
59     }
60     //然后我们就可以对类的对象进行+、-、*、/了。
61     void main(void)
62     {
63      A a1(1),a2(2),a3(3);
64      a1=a2+a3;
65      //或者
66      a1=a2.operator+(a3);
67     }

注意:在进行a2+a3的时候会出错,因为我们在上面对+定义了两种方法,去掉一种即可。

2.关系运算符重载

因为函数体比较简单,后面我就只给出成员函数形式的函数声明了,关系运算符有==,!=,<,>,<=,>=。

1     bool operator == (const A& ); 
2     bool operator != (const A& );
3     bool operator < (const A& );
4     bool operator <= (const A& );
5     bool operator > (const A& );
6     bool operator >= (const A& );

3.逻辑运算符重载

1     bool operator || (const A& );
2     bool operator && (const A& );
3     bool operator ! ();

4.单目运算符重载

这里的+、-是正负的意思,放在对象前面。

1     A& operator + ();
2     A& operator - ();
3     A* operator & ();
4     A& operator * ();

5.自增减运算符重载

++和–根据位置的不同有四种情况,都可以重载。

1     A& operator ++ ();//前置++
2     A operator ++ (int);//后置++
3     A& operator --();//前置--
4     A operator -- (int);//后置--

6.位运算符重载

按位操作。

1     A operator | (const A& );
2     A operator & (const A& );
3     A operator ^ (const A& );
4     A operator << (int i);
5     A operator >> (int i);
6     A operator ~ ();

7.赋值运算符重载

没有=哦。

 1     A& operator += (const A& );
 2     A& operator -= (const A& ); 
 3     A& operator *= (const A& );
 4     A& operator /= (const A& );
 5     A& operator %= (const A& );
 6     A& operator &= (const A& );
 7     A& operator |= (const A& );
 8     A& operator ^= (const A& );
 9     A& operator <<= (int i);
10     A& operator >>= (int i);

8.内存运算符重载

1     void *operator new(size_t size);
2     void *operator new(size_t size, int i);
3     void *operator new[](size_t size);
4     void operator delete(void*p);
5     void operator delete(void*p, int i, int j);
6     void operator delete [](void* p);

9.特殊运算符重载

上面的运算符重载都有两种方式,而下面的运算符只能用一种,特殊吧。 这些运算符的重载只能是成员函数。

 1     A& operator = (const A& );
 2     char operator [] (int i);//返回值不能作为左值
 3     const char* operator () ();
 4     T operator -> ();
 5     //类型转换符
 6     operator char* () const;
 7     operator int ();
 8     operator const char () const;
 9     operator short int () const;
10     operator long long () const;
11     //还有很多就不写了

而这些只能以友元函数的形式重载

1     friend inline ostream &operator << (ostream&, A&);//输出流
2     friend inline istream &operator >> (istream&, A&);//输入流

10.总结

两种重载方式的比较:

  • 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
  • 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
  • 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。 C++提供4个类型转换函数:reinterpret_cast(在编译期间实现转换)、const_cast(在编译期间实现转换)、stactic_cast(在编译期间实现转换)、dynamic_cast(在运行期间实现转换,并可以返回转换成功与否的标志)。
  • 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
  • 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
  • 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。
  • 当需要重载运算符具有可交换性时,选择重载为友元函数。

注意事项:

  1. 除了类属关系运算符”.“、成员指针运算符”.*“、作用域运算符”::“、sizeof运算符和三目运算符”?:“以外,C++中的所有运算符都可以重载。
  2. 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
  3. 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
  4. 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
  5. 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
  6. 运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LanceToBigData

JavaSE(四)之接口、访问控制

上面我们学习了几个修饰符,在开发中经常会用的到,所以必须熟练的掌握。接下来我学习一下接口和访问控制。 一、接口 一系列方法的声明,是一些方法特征的集合,一个接口...

25370
来自专栏鸿的学习笔记

python的对象引用

Every object has an identity, a type and a value. An object’s identity never cha...

13230
来自专栏十月梦想

变量和常亮

在函数内,变量的使用需要声明常量的作用域globay变量名(全局作用)local(局部)static(静态)

8720
来自专栏前端正义联盟

一道 js 闭包面试题的学习

最近看到一条有意思的闭包面试题,但是看到原文的解析,我自己觉得有点迷糊,所以自己重新做一下这条题目。

40140
来自专栏黑泽君的专栏

多态的两道练习题

30210
来自专栏鸿的学习笔记

Python对象的比较:is和==

这两个符号在Python的比较判断中应用广泛,但是这两者是有区别的,体现的是对象中的相等和标识符的概念。==符号比较的是两个对象是否相等,而is符号表达的则是标...

8020
来自专栏塔奇克马敲代码

第 15 章 面向对象程序设计

23730
来自专栏mukekeheart的iOS之旅

OC学习3——C语言特性之指针

1、指针是C语言中的一个非常重要的概念,实际上,OC系统类的变量、自定义类的变量等都是指针。定义指针变量的语法格式如下,其中*代表一个指针变量,整个语法代表定义...

27880
来自专栏个人分享

Scala语法笔记

JAVA中,举例你S是一个字符串,那么s(i)就是该字符串的第i个字符(s.charAt(i)).Scala中使用apply的方法

23020
来自专栏机器学习算法工程师

最长递增子序列

最长递增序列不要求数组元素连续问题,返回递增序列长度和递增序列。o(n^2)做法,顺序比较以第i个元素开头的递增序列即可。 利用动态规划来做,假设数组为1, -...

42660

扫码关注云+社区

领取腾讯云代金券