含所有运算符重载的讲解(附实例)
2016-03-16 17:22
429 查看
首先,来说一下什么时候会用到运算符的重载呢?
我们知道,一般的运算符只是满足了一部分数据类型的运算,并不能满足另外一些数据类型的操作。例如:类,我们想使用“+”这时就需要用到运算符的重载,使得“+”运算可以用到“类”之间的操作。诸如这样的情况,我们就要考虑使用运算符的重载了。
定义:赋予同一个运算符不同的操作方法。
C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作(针对新类型数据的实际需要对原有运算符进行的适当的改造)。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。
运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。
先来看一个包含所有运算符重载的实例(也可点击链接直接下载):点击打开链接
运算符函数定义的一般格式如下:
<返回类型说明符> operator <运算符符号>(<参数表>)
{
<函数体>
}
C++中除了三目运算符(?:), 作用域运算符 (::), 类属关系运算符 (.),成员指针 (.*)不能重载,sizeof运算符外,C++中的所有运算符都可以重载。
进行运算符重载前需要注意:(运算符即函数)
A)重载的运算操作数至少有一个是自定义数据;
B)只能重载已有的运算符;
C)所有运算符保持其优先级和结合;
D)运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
运算符函数重载的两种形式:
1、重载为类的成员函数
2、重载为类的非成员函数,非成员函数通常是友元。
(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)
成员函数运算符:
运算符重载为类的成员函数的一般格式为:
<函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数,那么用户自定义的操作数就是右边的那个了。因此:
(1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
(2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
(3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
调用成员函数运算符的格式如下:
<对象名>.operator <运算符>(<参数>)
它等价于
<对象名><运算符><参数>
例如:a+b等价于a.operator +(b)。一般情况下,我们采用运算符的习惯表达方式a.operator +(b)。
友元函数运算符:
friend <函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。
调用友元函数运算符的格式如下:
operator <运算符>(<参数1>,<参数2>)
它等价于
<参数1><运算符><参数2>
例如:a+b等价于operator +(a,b)。
两种重载形式的比较:
在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:
(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
(2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
(3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
(4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
(5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
(6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。
(7) 当需要重载运算符具有可交换性时,选择重载为友元函数。
备注:同样是对双目运算符进行操作,“成员函数运算符”只需要一个参数,另一个是类的一个对象,“友元函数运算符”则需要两个参数。
附加:浅拷贝和深复制
浅拷贝-即只是将对象的值复制一个副本给其他对象;修改其他对象,不改变原来的对象;
深拷贝-将对象的引用指针返回给其他对象;修改了其他对象,同样修改了原来对象
如果需防止拷贝:可以将含参构造函数和重载赋值运算符私有化即可。
各种运算符的重载:
http://blog.csdn.net/zuheyawen/article/details/7340717
我们知道,一般的运算符只是满足了一部分数据类型的运算,并不能满足另外一些数据类型的操作。例如:类,我们想使用“+”这时就需要用到运算符的重载,使得“+”运算可以用到“类”之间的操作。诸如这样的情况,我们就要考虑使用运算符的重载了。
定义:赋予同一个运算符不同的操作方法。
C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作(针对新类型数据的实际需要对原有运算符进行的适当的改造)。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。
运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。
先来看一个包含所有运算符重载的实例(也可点击链接直接下载):点击打开链接
//包含所有运算符重载的实例 #include "stdafx.h" #include <iostream> using namespace std; class Cfraction { private: int nume;//分子 int deno;//分母 public: Cfraction(int nu = 0, int de = 1)//构造函数,初始化用 { nume = nu; deno = de; } void simplify();//化简(使分子分母没有公因子) //输入输出重载 friend ostream& operator<<(ostream &out, const Cfraction &cf); friend istream& operator>>(istream &in, Cfraction &cf);///const是不能有的 //加减乘除,结果需要化简 friend Cfraction operator+(const Cfraction&lcf, const Cfraction&rcf); friend Cfraction operator-(const Cfraction&lcf, const Cfraction&rcf); friend Cfraction operator*(const Cfraction&lcf, const Cfraction&rcf); friend Cfraction operator/(const Cfraction&lcf, const Cfraction&rcf); //关系运算符 friend bool operator>(const Cfraction &lcf, const Cfraction &rcf); friend bool operator<(const Cfraction &lcf, const Cfraction &rcf); friend bool operator>=(const Cfraction &lcf, const Cfraction &rcf); friend bool operator<=(const Cfraction &lcf, const Cfraction &rcf); friend bool operator==(const Cfraction &lcf, const Cfraction &rcf); friend bool operator!=(const Cfraction &lcf, const Cfraction &rcf); //取+、-(正负,单目运算符) Cfraction operator+(); Cfraction operator-(); }; //简化分数 void Cfraction::simplify() { int v1 = nume; int v2 = deno; while (v2)//求最大公约数 { int temp = v2; v2 = v1%v2; v1 = temp; } nume /= v1; deno /= v1; if (deno < 0)//若分母小于0,将负号转移至分子,保证分母永远为正 { deno = -deno; nume = -nume; } } //输出重载 ostream& operator<<(ostream &out, const Cfraction &cf) { out << cf.nume << '/' << cf.deno; return out; } //输入重载 istream& operator>>(istream &in, Cfraction &cf) { char ch; while (1) { in >> cf.nume >> ch >> cf.deno; if (cf.deno == 0) { cerr << "分母为0请重新输入\n"; } else if (ch != '/') cerr << "格式错误(形如m/n)请重新输入\n"; else break; } return in; } //加法重载 Cfraction operator+(const Cfraction&lcf, const Cfraction &rcf) { Cfraction cf; cf.nume = lcf.nume*rcf.deno + lcf.deno + rcf.nume; cf.deno = lcf.deno*rcf.deno; cf.simplify(); return cf; } //减法重载 Cfraction operator-(const Cfraction&lcf, const Cfraction &rcf) { Cfraction cf; cf.nume = lcf.nume*rcf.deno - lcf.deno*rcf.nume; cf.deno = lcf.deno*rcf.deno; cf.simplify(); return cf; } //乘法重载 Cfraction operator*(const Cfraction&lcf, const Cfraction &rcf) { Cfraction cf; cf.nume = lcf.nume*rcf.nume; cf.deno = lcf.deno*rcf.deno; cf.simplify(); return cf; } //除法重载 Cfraction operator/(const Cfraction&lcf, const Cfraction &rcf) { Cfraction cf; cf.nume = lcf.nume*rcf.deno; cf.deno = lcf.deno*rcf.nume; cf.simplify(); return cf; } //取正重载 Cfraction Cfraction::operator + () { simplify(); if (nume < 0) { nume = -nume; } return *this; } //取负重载 Cfraction Cfraction::operator - () { simplify(); nume = -nume; return *this; } //大于号重载 bool operator>(const Cfraction &lcf, const Cfraction &rcf) { int l_nume = lcf.nume * rcf.deno; int r_nume = rcf.nume * lcf.deno; int common_deno = lcf.deno * rcf.deno; if ((l_nume - r_nume) * common_deno > 0) return true; return false; } //小于号重载 bool operator<(const Cfraction &lcf, const Cfraction &rcf) { return !(lcf > rcf); } //等于重载 bool operator==(const Cfraction &lcf, const Cfraction &rcf) { return lcf.nume == rcf.nume && lcf.deno == rcf.deno; } //不等于重载 bool operator!=(const Cfraction &lcf, const Cfraction &rcf) { return !(lcf == rcf); } //大于等于 bool operator>=(const Cfraction &lcf, const Cfraction &rcf) { if (lcf < rcf) return false; return true; } // bool operator<=(const Cfraction &lcf, const Cfraction &rcf) { if (lcf > rcf) return false; return true; } int main() { Cfraction cf1; Cfraction cf2; cout << "请输入两个分数:"; cin >> cf1 >> cf2; cout << "cf1:" << cf1 << "\t" << "cf2:" << cf2 << endl; cout << "cf1+cf2:" << cf1 + cf2 << endl; cout << "cf1-cf2:" << cf1 - cf2 << endl; cout << "cf1*cf2:" << cf1 * cf2 << endl; cout << "cf1/cf2:" << cf1 / cf2 << endl; cout << "+cf1:" << +cf1 << "-cf1:" << -cf1 << endl; cout << "+cf2:" << +cf2 << "-cf2:" << -cf2 << endl; cout << " cf1 > cf2? 1/YES 0/NO " << (cf1 > cf2) << endl << " cf1 < cf2? 1/YES 0/NO " << (cf1 < cf2) << endl << " cf1 != cf2? 1/YES 0/NO " << (cf1 != cf2) << endl << " cf1 == cf2? 1/YES 0/NO " << (cf1 == cf2) << endl << " cf1 >= cf2? 1/YES 0/NO " << (cf1 >= cf2) << endl << " cf1 <= cf2? 1/YES 0/NO " << (cf1 <= cf2) << endl; return 0; }
运算符函数定义的一般格式如下:
<返回类型说明符> operator <运算符符号>(<参数表>)
{
<函数体>
}
C++中除了三目运算符(?:), 作用域运算符 (::), 类属关系运算符 (.),成员指针 (.*)不能重载,sizeof运算符外,C++中的所有运算符都可以重载。
进行运算符重载前需要注意:(运算符即函数)
A)重载的运算操作数至少有一个是自定义数据;
B)只能重载已有的运算符;
C)所有运算符保持其优先级和结合;
D)运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
运算符函数重载的两种形式:
1、重载为类的成员函数
2、重载为类的非成员函数,非成员函数通常是友元。
(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)
成员函数运算符:
运算符重载为类的成员函数的一般格式为:
<函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数,那么用户自定义的操作数就是右边的那个了。因此:
(1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
(2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
(3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
调用成员函数运算符的格式如下:
<对象名>.operator <运算符>(<参数>)
它等价于
<对象名><运算符><参数>
例如:a+b等价于a.operator +(b)。一般情况下,我们采用运算符的习惯表达方式a.operator +(b)。
友元函数运算符:
friend <函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。
调用友元函数运算符的格式如下:
operator <运算符>(<参数1>,<参数2>)
它等价于
<参数1><运算符><参数2>
例如:a+b等价于operator +(a,b)。
#include "stdafx.h" #include <iostream> //不要加太多的头文件,可能会报错 using namespace std; class Complex { public: Complex(double r = 0.0, double i = 0.0) { real = r; image = i; } friend Complex operator+(const Complex &, const Complex &); void display(); private: double real; double image; }; Complex operator+(const Complex &c1, const Complex &c2) { return Complex(c1.real + c2.real, c1.image + c2.image); } void Complex::display() { cout <<real<<"+"<<"("<<image<<"i)"<< endl; } int main() { Complex c1(3, 4), c2(5, -10), c3; c3 = c1 + c2; cout << "c1="; c1.display(); cout << "c2="; c2.display(); cout << "c1+c2="; c3.display(); return 0; }</span>
两种重载形式的比较:
在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:
(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
(2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
(3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
(4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
(5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
(6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。
(7) 当需要重载运算符具有可交换性时,选择重载为友元函数。
备注:同样是对双目运算符进行操作,“成员函数运算符”只需要一个参数,另一个是类的一个对象,“友元函数运算符”则需要两个参数。
附加:浅拷贝和深复制
浅拷贝-即只是将对象的值复制一个副本给其他对象;修改其他对象,不改变原来的对象;
深拷贝-将对象的引用指针返回给其他对象;修改了其他对象,同样修改了原来对象
如果需防止拷贝:可以将含参构造函数和重载赋值运算符私有化即可。
各种运算符的重载:
http://blog.csdn.net/zuheyawen/article/details/7340717
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性