类的运算符重载
参考书籍 :C++ 世界的两本世界名著 [C++ 程序设计语言 ] 和 [C++primer]
重载知识点
1. 重载运算符
1.1 引言
1.2 限定及重载语法
1.2.1 限定
1.2.2 重载语法
1.3 重载运算符设计指导
1.4 成员 / 非成员函数重载
1.5 成员函数重载
1.6 非成员函数 ( 友元函数 ) 重载
1.7 重载实例
1.7.1 重载输入输出符 <<,>>
1.7.2 重载算法运算符 +,-
1.7.3 重载关系运算符 ==,!=,>,<=,<,>=
1.7.4 重载赋值运算符 =
1.7.5 重载下标结算符 []
1.7.6 重载前置自增 ++, 自减 -- 和重载后置自增 ++, 自减 --
1.7.7 重载成员访问符 -> 和 *
1.7.8 重载调用操作符 ()
1.7.9 重载转换操作符 operator (type)
*****************************************************************************
1 前言
重载运算符目的:
1. 用操作整数的各种运算方法 , 让运算符重载让类也具有整数的各种简单运算
2. 我们对类产生的对象进行非常方便的运算操作 , 让代码、简洁明了 , 操作方便
*****************************************************************************
2 定义
2.1 限定及重载语法
可以重载的运算符
+ - * / % ^
& | ~ ! = < >
+= -= *= /= %= ^= &= |=
<< >> >>= <<=
== != <= >=
&& ||
++ -- -> * , [] ()
new delete new[] delete[]
不可以重载的运算符
:: . .* ?: sizeof typeid
分类
算术运算符 + - * / % ^ ++ --
复合算术运算符 += -= *= /= %= ^=
位运算符 & | ~ !
复合位运算符 &= |=
逻辑运算符 && ||
关系运算符 == != <= < > >=
内存管理符 new delete new[] delete[]
输入输出运算符 << <<= >> >>=
*****************************************************************************
2.2 定义 重载语法
语法 :returntype operator op( 类参数 )
中文 : 返回类型 operator 运算符号 ( 参数 )
例子:
item operator+(const item&,const item&);
*****************************************************************************
2.3 成员函数实现操作符重载
使用情况:通常对类本身的运算符重载
如类 nameclass 的运算符函数名称 operator op
如 x,y 都是类 nameclass 的生成的对象 , 如果重载并实现了 operator op
我们可以做这样的计算 x op y, 编译器会自动翻译成如下 x.operator op(y)
如 op 是重载的运算符是加法 (+), 那么可变成 x+y, 编译器会变成 x.operator+(y)
计算顺序是:
x op y 变成 x=x op y, 运算结果存入到 x,
x.operator+(y)
第一个操作数是类的对象 x, 也就是隐含的对象指针 this,
第二个操作数是参数 y, 对象 X 与的数据成员逐个一一相加 ,
返回结果 : 是计算完毕后返回到左边的对象 , 它返回的必须是对象 , 而不能是其它数据类型
. 重载下标 [] 取值运算符实例
定义 operator[]
class foo
{
private:
vector<int> data;
public:
int& operator[] (const size_t);
};
实现 operator[]
int& foo::operator[](const size_t index)
{ return data[index]; }
*****************************************************************************
2.4 非成员函数 ( 友元函数 ) 实现操作符重载
使用情况:类的对象之间进行的算法运算符 , 关系运算符 , 输入输出
假设 # 是运算符号
类 nameclass 的友元函数重载名称 operator#
如 x,y 都是类 nameclass 的生成的对象 , 如果重载并实现了友元函数 operator#
我们可以做这样的计算 x # y, 编译器会自动翻译成如下 operator#(x,y)
计算顺序是:
x op y 变成 op(x,y), 运算结果存入到新的临时对象 ,operator+(x,y)
绝对无对象指针 this, 返回值不会存入到 x,y, 而是返回新的临时对象
第一个操作数是类的对象 x,
第二个操作数是参数 y,
对象 X 与 Y 的数据成员逐个一一相加 ,
返回结果 : 建立一个新的返回对象 , 如算术运算符返回另一个对象 , 关系运算符返回 bool 值
类的运算符重载的友元函数实现格式
友元函数定义格式
friend returntype operator op(const classname&,const classname&)const;
友元函数实现格式
returntype operator op(const classname&,const classname&)
{
// 代码控制
return (values);
}
*****************************************************************************
3 重载实例
3 .1 重载输入输出符 <<,>>
3 .2 重载算法运算符 -,*,/,%,^
3 .3 重载关系运算符 ==,!=
3 .4 重载赋值运算符 =
3 .5 重载下标结算符 []
3 .6 重载前置自增 ++, 自减 -- 和重载后置自增 ++, 自减 --
3 .7 重载成员访问符 -> 和 *
3 .8 重载调用操作符 ()
3 .9 重载转换操作符 operator (type)
*****************************************************************************
3 .1 重载 <<,>>
当我们对一个类重载 <<,>> 运算符之后 , 就可以直接使用 cout<<x,cin>>x 两种简单的操作
a. 重载输出运算符 <<
因重最左边的操作数是 ostream, 自然就不能用类的成员函数重载 , 而只能以类的友元函数进行重载
//<< 函数定义格式
friend ostream operator<<(ostream&,const nameclass&)const;
//<< 函数实现部分
ostream & operator<<(ostream& out,const classname& object)
{
//local delcare if any
//check object ostream
//output the members of the object
//...
return out;
}
b. 重载输出运算符 >>
因重最左边的操作数是 istream, 自然就不能用类的成员函数重载 , 而只能以类的友元函数进行重载
//<< 函数定义格式
friend istream operator>>(istream&,const nameclass&)const;
//<< 函数实现部分
istream & operator>>(istream& in,const classname& object)
{
//local delcare if any
//check object istream
//output the members of the object
//...
return in;
}
c.<<,>> 代码实现
// 重载函数 <<,>> 定义
class OpOver
{
public:
OpOver(int i=0;int j=0){a=i;b=j;};
friend ostream& operator<<(ostream&,const OpOver&);
friend istream& operator>>(istream&,const OpOver&);
OpOver operator+(const OpOver&)const;
bool operator==(const OpOver&)const;
private:
int a;
int b;
};
// 重载函数 <<,>> 实现
ostream& operator<<(ostream& out,const OpOver& right)
{
out<<right.a<<","<<right.b;
return out;
}
istream& operator>>(istream& in,const OpOver& right)
{
in>>right.a>>right.b;
return in;
}
***************************************************************************
3 .2 重载算法运算符 -,*,/,%,^
当我们对一个类重载 +,- 运算符之后 , 就可以直接使用 x+y,x-y 两种简单对象之间的加减操作
1.7.2.1 重载 +
因最左边的操作数是用类生成的新对象 , 自然重载 + 可以用成员函数也可以友元函数
//+ 重载函数用类的成员函数实现的定义格式
classname& operator+(const nameclass&,const nameclass&)const;
//+ 重载函数用类的成员函数实现的实现格式
classname& classname::operator+(const classname& left,const classname& right)
{
classname& tempclass(left); // 用左对象 left 建立一个新的对象 tempclass
tempclass+=right; // 临时对象 tempclass 与 right 的各个数据成员一一相加
// 上面这个代码要还要细化
return tempclass; // 返回运算结果到 left 的另一个副本 tempclass
}
3 .2.1 算术运算符重载 -,*,/,%,^
如同上面重载加法运算符一样,非常简单
只要在定义和实现,把 + 改成相应的其它算术运算符号就行啦
在函数实现部分只要把两个对象的各个成员分别进行相应的算术运算就行啦,
然后返回计算结果的临时对象就 OK 啦
实例代码
class OpOver
{
public:
OpOver(int i=0;int j=0){a=i;b=j;};
OpOver& operator+(const OpOver&)const;
OpOver& operator-(const OpOver&)const;
OpOver& operator*(const OpOver&)const;
OpOver& operator/(const OpOver&)const;
OpOver& operator%(const OpOver&)const;
private:
int a;
int b;
};
// 用类成员函数重载运算符 +
OpOver& OpOver::operator+(OpOver&& left,const OpOver& right)
{
OpOver& tempclass(left);
tempclass.a+=right.a;
tempclass.b+=right.b;
return tempclass;
}
// 用类成员函数重载运算符 -
OpOver& OpOver::operator-(OpOver&& left,const OpOver& right)
{
OpOver& tempclass(left);
tempclass.a-=right.a;
tempclass.b-=right.b;
return tempclass;
}
// 用类成员函数重载运算符 *
OpOver& OpOver::operator*(OpOver&& left,const OpOver& right)
{
OpOver& tempclass(left);
tempclass.a*=right.a;
tempclass.b*=right.b;
return tempclass;
}
// 用类成员函数重载运算符 /
OpOver& operator/(OpOver&& left,const OpOver& right)
{
// 要检查 right 的各个成员不能为 0
if (right.a!=0 && right.b!=0 )
{
OpOver& tempclass(left);
tempclass.a/=right.a;
tempclass.b/=right.b;
return tempclass;
}
}
// 用类成员函数重载运算符 %
OpOver& OpOver::operator%(OpOver&& left,const OpOver& right)
{
// 要检查 right 的各个成员不能为 0
if (right.a!=0 && right.b!=0 )
{
OpOver& tempclass(left);
tempclass.a%=right.a;
tempclass.b%=right.b;
return tempclass;
}
}
*****************************************************************************
3 .3 重载关系运算符 == , !=
重载关系运算符的编码指导,当实现一个关系运算符时,最好也实现此关系运算的反运算,
如成对实现 == 和 != , <= 和 > 成队实现, < 和 >= 成队实现
当我们对一个类重载 ==,!= 关系运算符之后 , 就可以直接使用 x==y,x!=y 两种简单对象之间的关系运算判断操作
3 . 3 .1 重载 ==
关系运算之后的结果是 bool, 自然重载 ==,!= 可以用成员函数也可以友元函数
== 重载函数用类的成员函数实现的定义格式
bool operator==(const nameclass&,const nameclass&)const;
//+ 重载函数用类的成员函数实现的实现格式
bool classname::operator==(const classname& left,const classname& right)
{
return (left. 成员 a==right. 成员 a && left. 成员 a==right. 成员 a && ...);
// 要比较两个对象之间所有的数据成员全部是否相等,结果相等返回 true, 不相等返回 false
}
代码实现
以上面的类 OpOver 为基础实现 == 重载关系运算
bool OpOver::operator==(const classname& left,const classname& right)
{
return (left. 成员 a==right. 成员 a && left. 成员 a==right. 成员 a );
}
以上面的类 OpOver 为基础实现 != 重载关系运算就更加简单,在 == 已经实现的基础上
实现重载 != 运算符的实现
bool OpOver::operator!=(const classname& left,const classname& right)
{
return !(left==right);
}
*****************************************************************************
3 .4 重载赋值运算符 =
它必须用类的成员函数来实现,而不能用友元函数
classname x,y;
x 各成员得到数据,而 y 的数据成员是空的 ...
y=x, 就把对象 x 中所有成员的数据复制到 y 的对应的成员里面
要让一个类的产生的不同以象可以直接进行赋值,就得重载并实现 = 赋值运算符
一般定义语法:
const classname& operator=(const classname&);
一般实现语法:
const classname& classname::operator=(const classname& other)
{
// 如果有局部变量,则定义
if(this!=other)
{
// 初始化 this 所指向的对象
// 复制 other 对象的所有成员的数据到 this 对象
}
else
{
// 如果 other 对象是空值,则直接把 this 对象置空
}
return *this; // 返回 this 指针所指向的引用对象
}
代码实例
class ilist
{
public:
ilist(int mszie=20);
const ilist& operator=(const ilist&); // 重载 = 运算符
void clearlist();
void print()const;
void insertitem(int item);
private:
int maxsize,length,int *list;
};
ilist::ilist(int msize)
{
length=0;
if (msize<=20)
maxsize=20;
else
maxsize=msize;
list= new int[maxsize];
assert(list!=NULL);
}
const ilist& ilist::operator=(const ilist& other)
{// 隐含使用 this 指针进行运算
if(this!=&other){
if (list!=NULL) {clearlist();} // 如果 this 所指的对象有数据,则清空它们
if (other.maxsize!=0) // 如果 other 引用的对象有数据,则复制它们到 this 指针指向的对象
{
list=new int[other.maxsize];
assert(list!=NULL);
for(int i=0;i!=other.length,i++)
list[i] =other.list[i];
}
}
else
{list=NULL;}
return *this;
}
*****************************************************************************
3 .5 重载下标结算符 []
它必须用类的成员函数来实现,而不能用友元函数
按位置索引快速的访问容器类的单个元素,容器如 string, 数组, vector 等
重载下标运算符 [] 设计方案
一般要实现两个 [] 运算符,一个用于访问加上 const ,一个用于修改不加 const.
定义 []
class demo
{
public:
int& operator[](const size_t); // 用于修改
const int& operator[](const size_t)const;
private:
vector<int> data;
};
实现 []
int& demo::operator[](const size_t index)
{ return data[index];}
const int& demo::operator[](const size_t index)const
{ return data[index];}
*****************************************************************************
3 .6 重载前置自增 ++, 自减 -- 和重载后置自增 ++, 自减 --
3 .6.1 引言
++ , -- 操作符经常用于迭代器这样的算法实现,在类中重载 ++ , -- 后,
类就提供了如同指针行为的方式来访问类序列中的元素,带访问检查的指针类可以实现任意类型的数组
在 ++ , -- 自然也仿照 int 运算,具有前置自增 ++, 自减 -- 和重载后置自增 ++, 自减 --
因编译器无法区分是前置还是后置, c++ 发明人就在后置 ++ , -- 引入一个识别标记 (int)
1.7.6.2 如下面的类实现 ++,-- 的运算符
class checkedptr
{
public:
checkdptr(int* b,int* e):beg(b),end(e),curr(b) {};
// 定义前置 ++ , --
checkdptr& operator++();
checkdptr& operator++();
// 定义后置 ++ , --
checkdptr operator++(int);
checkdptr operator++(int);
private:
int* beg,
int* end;
int* curr;
};
// 实现前置 ++ , --
// 表现形经常有 ++x,--y ,它通常是先变量加 1 ,然后引用此变量
checkdptr& checkedptr::operator++(){
if(curr==end)
throw out_of_ange(" 超出访问办界 ");
curr++;
return *this;
}
checkdptr& checkedptr::operator--(){
if(curr==beg)
throw out_of_ange(" 超出访问办界 ");
curr--;
return *this;
}
// 实现后置 ++ , -- ,
// 表现形经常有 x++,y-- ,它通常是先引用此变量,然后变量加 1
checkdptr checkedptr::operator++(int){
checkedptr old(*this); // 用类生成对象 old, 用 this 指针的对象去初始 old,old 保存初值
++(*this); //this 指向的对象 +1
return *old; // 返回调用 this 原来的初值
}
checkdptr checkedptr::operator--(int){
checkedptr old(*this); // 用类生成对象 old, 用 this 指针的对象去初始 old,old 保存初值
--(*this); //this 指向的对象 -1
return *old; // 返回调用 this 原来的初值
}
*****************************************************************************
3 .7 重载成员访问符 -> 和 *
引言
为了让类支持指针操作,方便高效的操作类的对象,充许重载 * 和 -> 两个操作符
* 常用用于构造智能指针,
1. 构建更安全的指针
用一个友元类来保存指针和使用计数,在 screenptr 最好一个对象消失后,自动删除基础对象
定义 scrptr 类
class scrptr{
private:
friend class screenptr; // 定义一个友元类 screenptr
screen *sp; // 定义一个指向 screen 的指针变量 sp
size_t use; // 定义一个 size_t 的整形变量 use
scrptr(screen *p):sp(p),use(1){} // 构造函数 use 初始化为 1 ,指针变量指始化为 p
~scrptr(){delete sp;} // 析构函数删除指针变量 sp
};
定义一个 screenptr 类将对 scrptr 类其指针进行使用计数,在 screenptr 最好一个对象消失后,自动删除基础对象
class screenptr{
public:
screenptr(screen *p):ptr(new scrptr(p)) {}
screenptr(const screenptr &orig):ptr(orig.ptr){(++ptr)->use;}
screenptr& operator=(const screenptr&);
~screenptr(){if ((--ptr)==0) delete ptr;} // 当 ptr 计数为 0 时,清空 ptr 指针
private:
scrptr* ptr;
};
使用此 screenptr 建立对象
screenptr ps(new screen(10,10));
2. 让类 screenptr 支持 -> 和 * 指针操作
在 screenptr 的 public: 修改为如下重载代码
class screenptr{
public:
screen& operator*(){return *ptr->sp;}
screen& operator->(){return ptr->sp;}
const screen operator*()const {return *ptr->sp;}
const screen operator->()const {return ptr->sp;}
private:
scrptr* ptr;
};
*****************************************************************************
3 .8 重载调用操作符 () // 也称函数对象
定义了调用操作符 () 的类,其对象常称为函数对象,它比函数更加灵活和方便
函数对象常用于通用算法的实参
可以为类的对象重载调用操作符 () ,如用结构实现的求绝对值的结构
struct abs_int
{
int operator()(int val){return val<=0?-val:val;}
};
如程序任务是返回长度是大于指定 6 个字符长度的函数的类
class GT_cls
{
public:
GT_cls(size_t val=0):bound(val){} // 构造函数,初始化私有成员 bound=0;
bool operator()(const string &s){return s.size()>=bound;}
private:
std::string szice_type bound;
};
当我们在使用此类时,就可以非常方便的使用
GT_cls(5);
GT_cls(6);
GT_cls(7);
*****************************************************************************
3 .9 重载转换操作符 operator (type)
引言
我们在操作 int,double 混合数据时 ,C++ 可以默认转换数据类型也可以强制进行类型转换
重载转换操作符就是为了方便对类产生的对象进行转换操作,它必须是成员函数实现
类的类型转换只能应用于一个转换,不能连续进行转换操作
语法格式
operator type() const {return type;}
如 operator int() const {return val;}// 转换为 int
如 operator float() const {return val;}// 转换为 float
*****************************************************************************
4 设计指导
4 .3.1 请尽量不重载具有内置意义的操作符
如没有重载合成赋值运算符 , 编译器为默认的进行逐个赋值 , 工作方式如同复制构造函数
建议不要重载常用的内置操作符如 &( 取地址 ),,( 逗号操作符 ),&&,||( 逻辑运算 )
4 .3.2 大多数操作符对类的对象没有意义
为类设计操作符 , 首先设计类的公用接口也就是公用函数 , 再用运算符重载函数来补充类的功能
相等测试请重载 ==, 重载 << 实现输出 , 重载 >> 实现输入 , 重载 ! 逻辑非 , 实现测试为对象是否为空
如字符串的 + 重载为对象连接运算 , 而不会重载为 int 的加法运算
4 .3.3 复合赋值操作符
如类重载算术操作符 (+,-,*,/,%,^,++,--), 就应重载对应复合算术运算 ,
如类重载位运算符 (&,|,~,!), 就应重载对应复合位运算符 (&=,|=)
如果重载了一组关系类型的运算符 , 就建议重载完关系运算符的其它符号
4 .3.4 成员函数和非成员函数 ( 友元函数 ) 选择方案
通常对类本身的操作 ,( 如赋值 =, 下标 [], 调用 (), 成员访问符 ->, 自增 ++, 自减 --) 请选择成员函数
通常对两个对象进行操作 ,( 如算术操作 , 位操作符 , 关系操作 , 输入输出 ) 请选择非成员函数 , 就是用友元函数实现
. 成员 / 非成员函数重载选择指导
一元操作符就是操作符有一个对象 , 如 x++,y-- 等 , 通常以类的成员函数实现重载
二元操作符就是操作符有二个对象 , 如 x+y,x-y 等 , 通常以类的友元函数实现重载
. 成员与非成员函数 ( 友元函数 ) 的选择指导
. 通常对类生成的对象进行运算 , 请选用成员函数实现 . 如赋值 =, 下标 [], 调用 (), 访问符 ->, 自增 ++, 自减 --, 复合位运算 +=,-=)
. 通常对类生成的多个对象进行运算 , 请选用非成员 ( 友元函数 ). 如类对象之间的 ( 算法运算符 , 关系运算符 , 输入输出符 )
. 当然也可以把 ( 算法运算符,关系运算符 ) 重载为类的成员函数 and 友元函数
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chinayaosir/archive/2008/01/14/2043039.aspx