C++用顶层函数重载操作符
几句话总结:以顶层函数的形式重载操作符时,二元操作符重载函数必须有两个参数,一元操作符重载必须有一个参数。以顶层函数的形式重载操作符时的好处是:
2用成员函数时会报错。具体看下面解释。但是用顶层函数不会。但是顶层函数实现时,不能直接调用类里面的私有成员,改进的方式是把顶层函数设为该类的友元函数。
在前面将操作符重载函数声明为类成员函数时,我们不断强调二元操作符,其函数参数为一个,一元操作符重载函数不需要函数参数。如果以顶层函数的形式重载操作符时,二元操作符重载函数必须有两个参数,一元操作符重载必须有一个参数。
以顶层函数的形式重载操作符,从函数实现上来看其实现相对于以类成员函数的形式实现起来要复杂一些,因为在类外无法直接访问类的私有成员变量。但是以顶层函数的形式来重载操作符有自身的优势,我们来看下面的示例。
本例中是以成员函数的形式进行操作符重载的,在主函数中我们定义了两个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 0; }我们再来看一下例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两个例子中,我们不难看出虽然实现麻烦的以顶层函数的形式进行操作符重载的优势所在了。我们总结一下,以类成员函数的形式进行操作符重载,操作符左侧的操作数必须为类对象;而以顶层函数的形式进行操作符重载,只要类中定义了相应的转型构造函数,操作符左侧或右侧的操作数均可以不是类对象,但其中必须至少有一个类对象,否则调用的就是系统内建的操作符而非自己定义的操作符重载函数了。
例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 0; }本例就是采用友元函数的形式进行操作符重载,如此实现既能继承操作符重载函数是顶层函数的优势,同时又能够使操作符重载函数实现起来更简单。
总结
以上是生活随笔为你收集整理的C++用顶层函数重载操作符的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: C++重载操作符的优先级
- 下一篇: C++ vector 容器浅析