(根据《C++程序设计》(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明)

对运算符重载的函数有两种处理方式:(1)把运算符重载的函数作为类的成员函数;(2)运算符重载的函数不是类的成员函数,在类中把它声明为友元函数。

1    把运算符重载函数作为类的成员函数

在上一篇博客(http://www.cnblogs.com/hust-ghtao/p/3525522.html)中,运算符重载函数 operator + 就是属于这种形式,它是Complex类中的成员函数。下面对这种方式进行分析。

例1:为了便于说明问题,将重载函数的定义重写如下:

   1: Complex Complex :: operator + ( Complex& c2 )

   2: {

   3:     Complex c ;

   4:     c.real = real + c2.real ;

   5:     c.imag = imag + c2.imag ;

   6:     return c ;

   7: }

有人可能会提出这样的疑问:“+”是双目运算符,为什么重载函数只有一个参数呢?实际上,运算符重载函数应当有两个参数,但是,由于重载函数是Complex类中的成员函数,因此有一个参数是隐含的,运算符函数是用this指针隐式的访问类对象的成员。可以看到operator+访问了两个对象中的成员,一个是this指针指向的对象中的成员,一个是形参对象中的成员。
2    把运算符重载函数作为类的友元函数
    运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数。在有关的类中把它声明为友元函数,即友元运算符重载函数。
    例2:将运算符+重载为适用于复数加法,重载函数不作为成员函数,而放在类外,作为Complex类的友元函数。
   1: class Complex

   2: {

   3: public:

   4:     ...

   5:     friend Complex operator + ( Complex& c1 , Complex& c2 ) ;    //重载函数作为友元函数

   6: private:

   7:     double real ;

   8:     double imag ;

   9: };

  10:  

  11: Complex operator + ( Complex& c1 , Complex& c2 )                    //定义运算符+重载函数

  12: {

  13:     Complex c ;

  14:     c.real = c1.real + c2.real ;

  15:     c.imag = c1.imag + c2.imag ;

  16:     return c ;

  17: }

这个程序和把运算符重载函数作为类的成员函数相比,只做了一处改动,就是将运算符重载函数作为类外的普通函数,并在Complex类中声明它为友元函数。可以看到运算符重载函数有两个参数。C++编译系统将程序中的表达式 c1 + c2 解释为operator + ( c1 , c2 )

有的读者可能会产生疑问:为什么把运算符函数作为友元函数?理由很简单,因为运算符函数要访问Complex类对象中的成员。如果运算符函数不是Complex类的友元函数,而是一个普通函数,它是没有权利访问Complex类的私有成员的。

3    将运算符重载函数作为类的成员函数和友元函数的区别

如果将运算符重在为类的成员函数,它可以通过this指针自由地访问本类的数据成员,因此至少可以少写一个函数的参数。但必须要求运算表达式中第一个参数(即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符的返回值与该对象同类型,运算结果才有意义。

例如在例1中,将运算符+重载为类的成员函数,则表达式 c1+c2中第一个参数c1是Complex类对象,运算符函数的返回值的类型也是Complex。

当然这种规定也会带来问题,如想将一个复数和一个整数相加,如c1+i,可以运算符+作为成员函数,形式如下:

   1: Complex Complex :: operator + ( int& i )

   2: { 

   3:     return Complex( real+i ,imag ) ;

   4: }

则在表达式必须是 c3 = c2 + i ;而不能写成c3 = i + c2 ;

如果要求程序员时刻记得重载运算符左右的参数类型,显然不是明智的选择。另外如果出于某种考虑,要求运算符左侧的操作数属于C++的标准类型或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数。如果需要访问类的私有成员,则必须声明为友元函数。

如还是将一个复数和一个整数相加,如c1+i,可以运算符+作为友元函数,形式如下:

在Complex类中声明:

   1: friend Complex operator + ( int& i , Complex& c ) ; //第一个参数可以不是类对象

   2: friend Complex operator + ( Complex& c , int& i ) ;

在类外定义友元函数:

   1: Complex operator + ( int& i , Complex& c )

   2: {

   3:     return Complex( i+c.real , c.imag ) ;

   4: }

   5:  

   6: Complex operator + (  Complex& c , int& i  )

   7: {

   8:     return Complex( i+c.real , c.imag ) ;

   9: }

经过这样的处理之后,表达式 c3 = i + c2 ;和 c3 = c2 + i ; 都是合法的。编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数。当然也可以把一个运算符重载函数作为友元函数,另一个作为成员函数。但不能两个都作为成员函数,原因显然哈。

究竟把运算符重载函数作为类的成员函数好,还是友元函数好?由于友元函数的使用会破坏类的封装,因此,从原则上说,要尽量将运算符函数作为成员函数。但应统筹考虑各方面的因素,以下提供具体原则,仅供参考:

(1)C++规定,赋值运算符“=”、下标运算符“[]”、函数调用运算符“()”、成员运算符“->”必须作为成员函数。

(2)流插入运算符“<<”和刘提取运算符“>>”、类型转换运算符只能作为友元函数。

(3)一般将单目运算符和复合运算符(+=、-=、/=、*=、!=、^=、%=、>>=、<<=)重载为成员函数。

(4)一般将双目运算符重载为友元函数。

C++学习之路—运算符重载(二)运算符重载作为类的成员函数和友元函数的更多相关文章

  1. C++运算符重载三种形式(成员函数,友元函数,普通函数)详解

    首先,介绍三种重载方式: //作为成员函数重载(常见) class Person{ Private: string name; int age; public: Person(const char* ...

  2. C++运算符重载形式——成员函数or友元函数

    运算符重载是C++多态的重要实现手段之一.通过运算符重载对运算符功能进行特殊定制,使其支持特定类型对象的运算,执行特定的功能,增强C++的扩展功能. 运算符重载的我们需要坚持四项基本原则: (1)不可 ...

  3. 重载运算符:类成员函数or友元函数

    类成员函数: bool operator ==(const point &a)const { return x==a.x; } 友元函数: friend bool operator ==(co ...

  4. FastAPI 学习之路(十二)接口几个额外信息和额外数据类型

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  5. #Java学习之路——基础阶段二(第一篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  6. #Java学习之路——基础阶段二(第十一篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  7. #Java学习之路——基础阶段二(第三篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  8. #Java学习之路——基础阶段二(第十篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  9. 学习之路三十二:VS调试的简单技巧

    这段时间园子里讲了一些关于VS的快捷键以及一些配置技巧,挺好的,大家一起学习,一起进步. 这段时间重点看了一下关于VS调试技巧方面的书,在此记录一下学习的内容吧,主要还是一些比较浅显的知识. 1. 调 ...

随机推荐

  1. POJ1985 DFS【STL__vector_的应用】

    vector     向量 相当于一个数组    在内存中分配一块连续的内存空间进行存储.支持不指定vector大小的存储.STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacit ...

  2. Qt中文件操作遇到的(变量,容器,结构体)

    咳咳!总结了一下我在使用QT文件操作时所用到的,再接再厉!再接再厉!! 1.保存到文件: QFile file("test.txt"); if (!file.open(QIODev ...

  3. Axis2(7):将Spring的装配JavaBean发布成WebService

    在现今的Web应用中经常使用Spring框架来装载JavaBean.如果要想将某些在Spring中装配的JavaBean发布成WebService,使用Axis2的Spring感知功能是非常容易做到的 ...

  4. java socket线程通信

    关于socket线程通信的一些知识整理 一般我们需要要让两台机子进行通信,需要创建一个Server 类,一个Client类,还需要创建一个线程类 server public class Server ...

  5. 无法启动outlook mapi

    office2013 管理员权限,在命令行中定位到office15文件夹,然后运行命令"outlook /importprf ..prf"

  6. Android圆形图片自定义控件

    Android圆形图片控件效果图如下: 代码如下: RoundImageView.java package com.dxd.roundimageview; import android.content ...

  7. Mysql主从配置+读写分离(转)

       MySQL从5.5版本开始,通过./configure进行编译配置方式已经被取消,取而代之的是cmake工具.因此,我们首先要在系统中源码编译安装cmake工具. 注:安装前须查看是否已经安装了 ...

  8. hdu4710

    Balls Rearrangement Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  9. C++界面库

    刚开始用C++做界面的时候,根本不知道怎么用简陋的MFC控件做出比较美观的界面,后来就开始逐渐接触到BCG  Xtreme ToolkitPro v15.0.1,Skin++,等界面库,以及一些网友自 ...

  10. 在Windows下用Eclipse+CDT+MinGW搭建C++开发平台

    本文提供了在Windows下用Eclipse+CDT+MinGW搭建C / C++开发平台的方法, 测试平台为Windows XP Sp2 CHS.   以下软件均为Windows平台下的版本. 1. ...