5.3 C++用顶层函数重载操作符
参考: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++用顶层函数重载操作符的更多相关文章
- C++重载操作符operator
operator是C++关键字,用于对C++进行扩展: 1.可以被重载的操作符:new,new[],delete,delete[],+,-,*,/,%,^,&,|,~,!,=,<,> ...
- VC6.0中重载操作符函数无法访问类的私有成员
整理日: 2015年03月18日 在 C++ 中,操作符(运算符)可以被重载以改写其实际操作.同时我们可以定义一个函数为类的朋友函数(friend function)以便使得这个函数能够访问类的私有成 ...
- C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象
重载操作符与转换 --调用操作符和函数对象 引言: 能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符! struct absInt { int operator() (int v ...
- C++基础 (4) 第四天 this指针 全局函数和成员函数 友元 操作符重载
1static强化练习-仓库进货和出货 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; c ...
- 《精通C#》索引器与重载操作符(11.1-11.2)
1.索引器方法结构大致为<modifier><return type> this [argument list],它可以在接口中定义: 在为接口声明索引器的时候,记住声明只是表 ...
- ActionScript语言函数重载
更新:你见过JavaScript支持重载吗,规范就是这么定义的.如果不是研究Java和Flex对象的Serialization,我也不会注意它. 距离写这篇文章已有8年了,时光匆匆啊,今天整理资料时看 ...
- C++ 重载操作符与转换
<C++ Primer 4th>读书笔记 重载操作符是具有特殊名称的函数:保留字 operator 后接需定义的操作符号. Sales_item operator+(const Sales ...
- c++函数重载---2
原创博客:转载请标明出处:http://www.cnblogs.com/zxouxuewei/ 写在前面: 函数重载的重要性不言而明,但是你知道C++中函数重载是如何实现的呢(虽然本文谈的是C++中函 ...
- C++ 函数重载与函数匹配
<C++ Primer>笔记,整理关于函数重载与函数匹配的笔记. 函数重载 void func(int a); //原函数 void func(double a); //正确:形参类型不同 ...
随机推荐
- Windows 下 Redis 服务无法启动,错误 1067 进程意外终止解决方案
1.检查端口是否被占用 2.修改 Windows 服务里的 Redis 服务为本地系统服务(修改方式见下文) 方法: 1.看系统日志 桌面计算机/此电脑(Win10名称)右键打开管理,或 Win+R ...
- English trip V1 - B 15. Giving Personal Information 提供个人信息 Teacher:Solo Key: Do/Does
In this lesson you will learn to answer simple questions about yourself. 本节课讲学到回答关于自己的一些简单问题 课上内容(L ...
- 学习Py——自己模拟写的一个Range功能
#!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = "loki" # function: Modeled range ...
- 20181013xlVba年级报表拆分为班级报表
'年级报表拆分为班级报表 Public Sub CreateClassReport() Application.DisplayAlerts = False Dim Wb As Workbook Dim ...
- Confluence 6 查看所有空间
有下面 2 种方法在 Confluence 中查看空间: 空间目录(The space directory) – 在 Confluence 的头部选择 空间(Spaces )> 空间目录(Spa ...
- 微信小程序 使用环信聊天工具
当时做微信小程序环信的时候,没有遇到太多的问题,因为找到了一个例子,有兴趣的朋友可以把这个包下载下来看一下,写的超级的号,使用起来也特别简单,appkey需要自己配置,从环信官网https://con ...
- css之transform属性
定义元素的旋转(rotate),缩放(scale),移动(translate),倾斜(skew) rotate rotate(angle) 定义 2D 旋转,在参数中规定角度. rotate3d(x, ...
- BottomNavigationBar使用详解
gitHub地址:https://github.com/Ashok-Varma/BottomNavigation 一.基本使用 1.在AndroidStudio下添加依赖: compile 'com. ...
- Integer To Roman leetcode java
问题描述: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range fr ...
- IntelliJ IDEA的调试方法
快捷键F9 resume programe 恢复程序 Alt+F10 show execution point 显示执行断点 F8 S ...