参考:http://www.weixueyuan.net/view/6381.html

总结:

  可以将操作符重载函数声明为顶层函数。

  如果以顶层函数的形式重载操作符时,二元操作符重载函数必须有两个参数,一元操作符重载必须有一个参数。

  加法操作符重载函数的函数头complex operator+(const complex & A, const complex &B),首先因为加法操作符重载后可以计算复数的加法,返回的仍然是一个复数。

  需要注意的是指针操作符“->”、下标操作符“[]”、函数调用操作符“()”和赋值操作符“=”只能以成员函数的形式进行操作符重载。

  以顶层函数的形式重载操作符,从函数实现上来看其实现相对于以类成员函数的形式实现起来要复杂一些,因为在类外无法直接访问类的私有成员变量。但是以顶层函数的形式来重载操作符有自身的优势。如下。

  以类成员函数的形式进行操作符重载,操作符左侧的操作数必须为类对象;而以顶层函数的形式进行操作符重载,只要类中定义了相应的转型构造函数,操作符左侧或右侧的操作数均可以不是类对象,但其中必须至少有一个类对象,否则调用的就是系统内建的操作符而非自己定义的操作符重载函数了。

  如果我们将操作符重载函数这些顶层函数声明为类的友元函数,那么就可以直接访问类的私有成员变量了。 

  采用友元函数的形式进行操作符重载,如此实现既能继承操作符重载函数是顶层函数的优势,同时又能够使操作符重载函数实现起来更简单。

---------------------------

在前面两节中,我们是将操作符重载函数声明为类的成员函数,其实除了能将操作符重载函数声明为类的成员函数之外,我们也可以将操作符重载函数声明为顶层函数。在前面将操作符重载函数声明为类成员函数时,我们不断强调二元操作符,其函数参数为一个,一元操作符重载函数不需要函数参数。但是一旦将操作符重载函数声明为顶层函数时,则必须至少有一个类对象参数,否则的话编译器无法区分操作符是系统内建的还是程序设计人员自己定义的,有了一个类对象参数之后,系统则会根据情况调用内建或自定的操作符。如果以顶层函数的形式重载操作符时,二元操作符重载函数必须有两个参数,一元操作符重载必须有一个参数。

例1:

#include <iostream>
using namespace std; class complex
{
public:
complex();
complex(double a);
complex(double a, double b);
double getreal() const { return real; }
double getimag() const { return imag; }
void setreal(double a){ real = a; }
void setimag(double b){ imag = b; }
void display()const;
private:
double real; //复数的实部
double imag; //复数的虚部
}; complex::complex()
{
real = 0.0;
imag = 0.0;
} complex::complex(double a)
{
real = a;
imag = 0.0;
} complex::complex(double a, double b)
{
real = a;
imag = b;
} //打印复数
void complex::display()const
{
cout<<real<<" + "<<imag<<" i ";
} //重载加法操作符
complex operator+(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() + B.getreal());
C.setimag(A.getimag() + B.getimag());
return C;
} //重载减法操作符
complex operator-(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() - B.getreal());
C.setimag(A.getimag() - B.getimag());
return C;
} //重载乘法操作符
complex operator*(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() * B.getreal() - A.getimag() * B.getimag() );
C.setimag(A.getimag() * B.getreal() + A.getreal() * B.getimag() );
return C;
} //重载除法操作符
complex operator/(const complex & A, const complex & B)
{
complex C;
double square = A.getreal() * A.getreal() + A.getimag() * A.getimag();
C.setreal((A.getreal() * B.getreal() + A.getimag() * B.getimag())/square);
C.setimag((A.getimag() * B.getreal() - A.getreal() * B.getimag())/square);
return C;
} int main()
{
complex c1(4.3, -5.8);
complex c2(8.4, 6.7);
complex c3; c3 = c1 + c2;
cout<<"c1 + c2 = ";
c3.display();
cout<<endl; c3 = c1 - c2;
cout<<"c1 - c2 = ";
c3.display();
cout<<endl; c3 = c1 * c2;
cout<<"c1 * c2 = ";
c3.display();
cout<<endl; c3 = c1 / c2;
cout<<"c1 / c2 = ";
c3.display();
cout<<endl; return ;
}
如本例所示,本例则是用顶层函数重载的加法、减法、乘法和除法操作符,使之分别具有加减乘除功能。因为是以顶层函数的形式重载操作符的,因此类中没有声明操作符重载函数。为了能够在类外操作real和imag两个数据成员,我们为类添加了getimag、getreal、setimag和setreal函数。我们以加法操作符的重载为例来看普通操作符重载函数如何作为顶层函数。

加法操作符重载函数的函数头complex operator+(const complex & A, const complex &B),首先因为加法操作符重载后可以计算复数的加法,返回的仍然是一个复数,因此该函数的返回值仍然是complex。操作符重载函数参数为两个complex类对象的引用,加法操作符为二元操作符,因此必须要有两个操作数,因此函数有两个参数。函数体部分比较简单,只是使用getimag、getreal、setimag和setreal四个函数来实现复数的加法操作而已。

其它普通操作符的重载与例1中的加法操作符重载类似。如果我们重载的是一元操作符,则函数需要有一个参数。

例2:

class test
{
//......
}; test operator!(test & A)
{
//......
}

本例中以顶层函数的形式重载非操作符符,因为其为一元操作符,故而函数有一个参数。

以顶层函数的形式重载操作符,其调用方法与普通函数调用类似。

例如本节中的例1中的复数类,我们调用加法重载函数时可以采用如下方法:
    complex c1, c2, c3;
    c3 = operator+( c1, c2);
这样的函数调用方法和普通的函数调用方法一样,但是由于operator关键字的作用,我们还可以采用另外一种我们熟知的调用方法:
    c3 = c1 + c2;
这种调用方法和先前以类成员函数的形式重载操作符调用方法一直。本节例1中也都是采用这种简单明了的调用方法。

需要注意的是指针操作符“->”、下标操作符“[]”、函数调用操作符“()”和赋值操作符“=”只能以成员函数的形式进行操作符重载。

以顶层函数的形式重载操作符,从函数实现上来看其实现相对于以类成员函数的形式实现起来要复杂一些,因为在类外无法直接访问类的私有成员变量。但是以顶层函数的形式来重载操作符有自身的优势,我们来看下面的示例。

例3:

#include <iostream>
using namespace std; class complex
{
public:
complex();
complex(double a);
complex(double a, double b);
complex operator+(const complex & A)const;
complex operator-(const complex & A)const;
complex operator*(const complex & A)const;
complex operator/(const complex & A)const;
void display()const;
private:
double real; //复数的实部
double imag; //复数的虚部
}; complex::complex()
{
real = 0.0;
imag = 0.0;
} complex::complex(double a)
{
real = a;
imag = 0.0;
} complex::complex(double a, double b)
{
real = a;
imag = b;
} //打印复数
void complex::display()const
{
cout<<real<<" + "<<imag<<" i ";
} //重载加法操作符
complex complex::operator+(const complex & A)const
{
complex B;
B.real = real + A.real;
B.imag = imag + A.imag;
return B;
} //重载减法操作符
complex complex::operator-(const complex & A)const
{
complex B;
B.real = real - A.real;
B.imag = imag - A.imag;
return B;
} //重载乘法操作符
complex complex::operator*(const complex & A)const
{
complex B;
B.real = real * A.real - imag * A.imag;
B.imag = imag * A.real + real * A.imag;
return B;
} //重载除法操作符
complex complex::operator/(const complex & A)const
{
complex B;
double square = A.real * A.real + A.imag * A.imag;
B.real = (real * A.real + imag * A.imag)/square;
B.imag = (imag * A.real - real * A.imag)/square;
return B;
} int main()
{
complex c1, c2(15.5, 23.1);
c1 = c2 + 13.5;
c1 = 13.5 + c2;
return ;
}

本例中是以成员函数的形式进行操作符重载的,在主函数中我们定义了两个complex复数类的对象,语句“c1 = c2 + 13.5;”是将c2与一个double类型的数据相加,我们可以将其理解为:
    c1 = c2.operator+(13.5);
因为我们在类中定义了一个只带一个参数的构造函数complex(double a);,这个构造函数其实可以视为转型构造函数,它可以将double类型转换为一个complex类对象。因此 “c1 = c2 + 13.5;”语句其实也是相当于两个复数类对象相加。当然,如果在类中没有定义complex(double a);这样一个只带一个参数的构造函数,那么这一句也是有语法问题的,因为我们重载的加法只适用于两个complex类对象相加,而系统内建的又只能用于两个普通数据类型的操作数相加,一个complex类对象和一个普通数据类型的操作数相加,系统是无法去处理这样的异常情况的。

我们再来看一下后面一个语句“c1 = 13.5 + c2;”,这一语句我们可以将其理解为:
    c1 = 13.5.operator+(c2);
如此一来,这一句的问题非常明显,13.5只是一个double类型的常数,它不是类对象,因此也不可能有调用operator+的能力。虽然我们在类中定义了一个具有一个参数的构造函数,但是编译器将语句“c1 = 13.5 + c2;”理解成“c1 = 13.5.operator+(c2);”并不会将13.5转换成一个complex类对象,因为编译器遇到这种情况并不会产生一种很智能的处理,同样它也并不知道程序设计人员的意图。所以例3中语句“c1 = 13.5 + c2;”是有语法错误的。

例4:

#include <iostream>
using namespace std; class complex
{
public:
complex();
complex(double a);
complex(double a, double b);
double getreal() const { return real; }
double getimag() const { return imag; }
void setreal(double a){ real = a; }
void setimag(double b){ imag = b; }
void display()const;
private:
double real; //复数的实部
double imag; //复数的虚部
}; complex::complex()
{
real = 0.0;
imag = 0.0;
} complex::complex(double a)
{
real = a;
imag = 0.0;
} complex::complex(double a, double b)
{
real = a;
imag = b;
} //打印复数
void complex::display()const
{
cout<<real<<" + "<<imag<<" i ";
} //重载加法操作符
complex operator+(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() + B.getreal());
C.setimag(A.getimag() + B.getimag());
return C;
} //重载减法操作符
complex operator-(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() - B.getreal());
C.setimag(A.getimag() - B.getimag());
return C;
} //重载乘法操作符
complex operator*(const complex & A, const complex &B)
{
complex C;
C.setreal(A.getreal() * B.getreal() - A.getimag() * B.getimag() );
C.setimag(A.getimag() * B.getreal() + A.getreal() * B.getimag() );
return C;
} //重载除法操作符
complex operator/(const complex & A, const complex & B)
{
complex C;
double square = A.getreal() * A.getreal() + A.getimag() * A.getimag();
C.setreal((A.getreal() * B.getreal() + A.getimag() * B.getimag())/square);
C.setimag((A.getimag() * B.getreal() - A.getreal() * B.getimag())/square);
return C;
} int main()
{
complex c1, c2(15.5, 23.1);
c1 = c2 + 13.5;
c1 = 13.5 + c2;
return ;
}
我们再来看一下例4,这个例子则是以顶层函数的形式定义操作符重载函数。我们同样来看主函数,主函数定义了c1和c2两个complex类对象。先来看一下语句“c1 = c2 + 13.5;”,这个语句可以理解如下:

c1 = operator+(c2, 13.5);
因为我们在顶层函数中定义了complex operator+(const complex & A, const complex &B)函数,系统在执行“c1 = operator+(c2, 13.5);”时找到了对应的顶层函数,但是发现参数不对,但是可以通过类的构造函数将13.5转换成complex类对象,如此就满足operator+函数的调用条件了,故而这一句是没有问题的。

我们再来看一下语句“c1 = 13.5 + c2;”,这一语句可以理解为:
    c1 = operator+(13.5, c2);
这一句的执行与“c1 = operator+(c2, 13.5);”是一样的,它可以利用类的构造函数将13.5转换为complex类对象,因此这一句也是可以正确执行的。

从例3和例4两个例子中,我们不难看出虽然实现麻烦的以顶层函数的形式进行操作符重载的优势所在了。我们总结一下,以类成员函数的形式进行操作符重载,操作符左侧的操作数必须为类对象;而以顶层函数的形式进行操作符重载,只要类中定义了相应的转型构造函数,操作符左侧或右侧的操作数均可以不是类对象,但其中必须至少有一个类对象,否则调用的就是系统内建的操作符而非自己定义的操作符重载函数了。

在例4中,我们以顶层函数的形式进行操作符重载,但是因为无法直接访问complex类中的私有成员,故而我们在类中增添了getimag、getreal、setimag和setreal函数以操作类中的私有成员变量,如此一来实现这些操作符重载函数看上去就有些复杂了,不是那么直观。除了此种方法以外,我们还可以将complex类中的私有成员real和imag声明为public属性,但是如此一来就有悖类的信息隐藏机制了。除了这两种方法外,我们是否还有其它方法解决这个问题呢?

有,还有一种方法。在前面章节中我们介绍过友元函数,如果我们将操作符重载函数这些顶层函数声明为类的友元函数,那么就可以直接访问类的私有成员变量了。

例5:

#include <iostream>
using namespace std; class complex
{
public:
complex();
complex(double a);
complex(double a, double b);
friend complex operator+(const complex & A, const complex & B);
friend complex operator-(const complex & A, const complex & B);
friend complex operator*(const complex & A, const complex & B);
friend complex operator/(const complex & A, const complex & B);
void display()const;
private:
double real; //复数的实部
double imag; //复数的虚部
}; complex::complex()
{
real = 0.0;
imag = 0.0;
} complex::complex(double a)
{
real = a;
imag = 0.0;
} complex::complex(double a, double b)
{
real = a;
imag = b;
} //打印复数
void complex::display()const
{
cout<<real<<" + "<<imag<<" i ";
} //重载加法操作符
complex operator+(const complex & A, const complex &B)
{
complex C;
C.real = A.real + B.real;
C.imag = A.imag + B.imag;
return C;
} //重载减法操作符
complex operator-(const complex & A, const complex &B)
{
complex C;
C.real = A.real - B.real;
C.imag = A.imag - B.imag;
return C;
} //重载乘法操作符
complex operator*(const complex & A, const complex &B)
{
complex C;
C.real = A.real * B.real - A.imag * B.imag;
C.imag = A.imag * B.real + A.real * B.imag;
return C;
} //重载除法操作符
complex operator/(const complex & A, const complex & B)
{
complex C;
double square = A.real * A.real + A.imag * A.imag;
C.real = (A.real * B.real + A.imag * B.imag)/square;
C.imag = (A.imag * B.real - A.real * B.imag)/square;
return C;
} int main()
{
complex c1(4.3, -5.8);
complex c2(8.4, 6.7);
complex c3; c3 = c1 + c2;
cout<<"c1 + c2 = ";
c3.display();
cout<<endl; c3 = c1 - c2;
cout<<"c1 - c2 = ";
c3.display();
cout<<endl; c3 = c1 * c2;
cout<<"c1 * c2 = ";
c3.display();
cout<<endl; c3 = c1 / c2;
cout<<"c1 / c2 = ";
c3.display();
cout<<endl; return ;
}

本例就是采用友元函数的形式进行操作符重载,如此实现既能继承操作符重载函数是顶层函数的优势,同时又能够使操作符重载函数实现起来更简单。

5.3 C++用顶层函数重载操作符的更多相关文章

  1. C++重载操作符operator

    operator是C++关键字,用于对C++进行扩展: 1.可以被重载的操作符:new,new[],delete,delete[],+,-,*,/,%,^,&,|,~,!,=,<,> ...

  2. VC6.0中重载操作符函数无法访问类的私有成员

    整理日: 2015年03月18日 在 C++ 中,操作符(运算符)可以被重载以改写其实际操作.同时我们可以定义一个函数为类的朋友函数(friend function)以便使得这个函数能够访问类的私有成 ...

  3. C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象

    重载操作符与转换 --调用操作符和函数对象 引言: 能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符! struct absInt { int operator() (int v ...

  4. C++基础 (4) 第四天 this指针 全局函数和成员函数 友元 操作符重载

    1static强化练习-仓库进货和出货 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; c ...

  5. 《精通C#》索引器与重载操作符(11.1-11.2)

    1.索引器方法结构大致为<modifier><return type> this [argument list],它可以在接口中定义: 在为接口声明索引器的时候,记住声明只是表 ...

  6. ActionScript语言函数重载

    更新:你见过JavaScript支持重载吗,规范就是这么定义的.如果不是研究Java和Flex对象的Serialization,我也不会注意它. 距离写这篇文章已有8年了,时光匆匆啊,今天整理资料时看 ...

  7. C++ 重载操作符与转换

    <C++ Primer 4th>读书笔记 重载操作符是具有特殊名称的函数:保留字 operator 后接需定义的操作符号. Sales_item operator+(const Sales ...

  8. c++函数重载---2

    原创博客:转载请标明出处:http://www.cnblogs.com/zxouxuewei/ 写在前面: 函数重载的重要性不言而明,但是你知道C++中函数重载是如何实现的呢(虽然本文谈的是C++中函 ...

  9. C++ 函数重载与函数匹配

    <C++ Primer>笔记,整理关于函数重载与函数匹配的笔记. 函数重载 void func(int a); //原函数 void func(double a); //正确:形参类型不同 ...

随机推荐

  1. Abstract Class 一些要点

    抽象类不能使用new operator创建实例. 抽象方法无需实现即可定义.其实现由子类完成. 包含抽象方法的类必须被定义成抽象的. 抽象类的构造函数可以定义成protected,因为它只会被子类使用 ...

  2. Centos6.8 smokeping安装

    yum -y install rrdtool perl-rrdtool curl perl-core bind bind-chroot bind-utils httpd popt popt-devel ...

  3. 使用visual studio把xsd文件转成xml格式文件

    使用visual studio把xsd文件转成xml格式文件 最近一段时间都在做Amazon的mws api的对接工作,mws api的描述文件都是使用的xsd文件来进行的,之前确实也没有接触过,也花 ...

  4. python2.x编码问题实例

    1,编码问题,主要是区分面向人类的字符串,面向计算机的字节序列 在python3中,字符串是str(默认即unicode),字节序列是bytes 在python2中,字符串是unicode,字节序列是 ...

  5. 20170822xlVBA ExportCellPhone

    Public Sub GetCellPhone() Dim CellPhone As String Dim Arr As Variant Dim Brr As Variant Dim n As Lon ...

  6. CF1114E Arithmetic Progression

    给定一个打乱的等差数列,每次两种操作. 1.查询一个位置. 2.查询是否有比x大的数字. 一共60次操作. sol: 30次操作即可二分出首项. 剩下30次操作查询出30个位置然后两两做差取gcd即可 ...

  7. bzoj1801: [Ahoi2009]chess 中国象棋 dp

    题意:在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧. 题解:dp[i][j][k]表示到了第i行,有j列 ...

  8. python-day75--django项目问题详细

    1.项目名要小写 2.表中字段 AutoField()   表示整形字段,建表时不用写,当整形的数字范围不够你用的时候, 你可以用 BigAutoField()字段, 表示长整形    当表内新增有关 ...

  9. Yii2框架RESTful API教程

    前不久做一个项目,是用Yii2框架写一套RESTful风格的API,就去查了下<Yii 2.0 权威指南 >,发现上面写得比较简略.所以就在这里写一篇教程贴,希望帮助刚接触Yii2框架RE ...

  10. Linux系统(X64)7 安装Oracle11g完整安装图文教程另附基本操作

    在linux 7.6 安装 oracle 11g    mount 挂载yum源 yum –y sys*  gcc*  lib* sys* ma* un* gli* elf* bin* com*   ...