12.C++类的运算符重载

来源:互联网 发布:js date函数 编辑:程序博客网 时间:2024/05/16 17:46

12.C++类的运算符重载

分类: 6.对象编程-Java/C++ 1841人阅读 评论(0)收藏 举报
classdelete编译器object算法list
 
类的运算符重载
chinanetboy个人总结,10/21/2007笔记
(前言,定义,重载实例,设计指导)
参考书籍: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,
对象XY的数据成员逐个一一相加,
返回结果:建立一个新的返回对象,如算术运算符返回另一个对象,关系运算符返回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;  //
临时对象tempclassright的各个数据成员一一相加
//
上面这个代码要还要细化
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支持->*指针操作
screenptrpublic:修改为如下重载代码
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 友元函数
*****************************************************************************
0 0
原创粉丝点击