类的运算符重载
- 格式:docx
- 大小:30.63 KB
- 文档页数:19
类的运算符重载参考书籍: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 的各个成员不能为0if (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 的各个成员不能为0if (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;elsemaxsize=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-- ,它通常是先引用此变量,然后变量加1checkdptr checkedptr::operator++(int){checkedptr old(*this); // 用类生成对象old, 用this 指针的对象去初始old,old 保存初值 ++(*this); //this 指向的对象+1return *old; // 返回调用this 原来的初值}checkdptr checkedptr::operator--(int){checkedptr old(*this); // 用类生成对象old, 用this 指针的对象去初始old,old 保存初值 --(*this); //this 指向的对象-1return *old; // 返回调用this 原来的初值}*****************************************************************************3 .7 重载成员访问符->和*引言为了让类支持指针操作,方便高效的操作类的对象,充许重载* 和->两个操作符* 常用用于构造智能指针,1. 构建更安全的指针用一个友元类来保存指针和使用计数,在screenptr 最好一个对象消失后,自动删除基础对象定义scrptr 类class scrptr{private:friend class screenptr; // 定义一个友元类screenptrscreen *sp; // 定义一个指向screen 的指针变量spsize_t use; // 定义一个size_t 的整形变量usescrptr(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 友元函数#include<iostream>using namespace std;#include "str.h"void displayString(MyString ms){for(int i=0; i<ms.getLength(); i++){cout<<ms[i];}cout<<endl;}int main(){char stra[]="ABCD";MyString s1("abcd");MyString s2(s1);MyString s3(stra);MyString s4;s4=s3;s1.display();s2.display();s3.display();s4.display();cout<<"----------------------------------------------------"<<endl; s4 = s3.padLeft(10, '*');s3.display();s4.display();cout<<"----------------------------------------------------"<<endl; MyString s5 = s4.remove(0, 3);s4.display();s5.display();cout<<"----------------------------------------------------"<<endl; s4=s1+s3;displayString(s4);if (s1<s2)cout<<"s1<s2"<<endl;else if (s1==s2)cout <<"s1=s2"<<endl;elsecout<<"s1>s2"<<endl;if (s1<s4)cout<<"s1<s4"<<endl;else if (s1==s4)cout <<"s1=s4"<<endl;elsecout<<"s1>s4"<<endl;return 0;}//str.hclass MyString {private:char *Str;int len;public:int getLength(){return len;}void display(){cout<<"string:"<<Str<<",length:"<<len<<endl;}MyString(){len=0;Str=NULL;}MyString(const char *p){len=strlen(p);Str=new char[len+1];strcpy(Str,p);}~MyString(){if (Str!=NULL){delete [] Str;Str=NULL;}}MyString(MyString & r){len=r.len;if(len!=0){Str=new char[len+1];strcpy(Str,r.Str);}}MyString operator =(MyString r) {len=r.len;if(len!=0){Str=new char[len+1];strcpy(Str,r.Str);}return *this;}char &operator[](int n){return Str[n];}MyString operator +(MyString str2){MyString strResult;int length = len + str2.len;if(length!=0){strResult.Str=new char[length+1];}strcpy(strResult.Str, Str);strcat(strResult.Str, str2.Str); strResult.len = length;return strResult;}bool operator <(MyString str2){if( strcmp (Str, str2.Str) < 0)return true;elsereturn false;}MyString padLeft(int n, char padding=' ') {MyString strResult;if(n!=0){strResult.Str=new char[n+1];}int src_length = strlen(Str);int i;for (i = 0; i < n - src_length; ++i) strResult.Str[i] = padding;strncpy(strResult.Str + i, Str, src_length); strResult.Str[n] = '\0';strResult.len = strlen(strResult.Str); return strResult;}......}。