第8章 运算符重载_第1页
第8章 运算符重载_第2页
第8章 运算符重载_第3页
第8章 运算符重载_第4页
第8章 运算符重载_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章运算符重载8.1概述运算符只能用于标准类型的数据的运算,而不能用于类的对象。如果使用运算符对类的对象进行运算,就必须对运算符进行重载。8.1.1什么是运算符重载在2.4.3节中,谈到通过定义多个同名函数可以实现函数的重载。在C++语言中,运算符也被看成一种特殊的函数,因此也能够被重载。在前面已经接触过很多运算符重载的例子。例如:cout<<"Helloworld!";cout<<56;在这里,同一个运算符“<<”,完成了两种不同类型数据的输出,这就是通过对运算符“<<”重载的结果。运算符除了可以用于操作标准数据类型的数据外,也可以通过重载来操作类的对象。8.1.2运算符重载规则1.C++语言中允许重载的运算符C++语言中大部分运算符都可以重载,如表8.1所示,表中列出了所有允许重载的运算符。C++语言中不允许重载的运算符只有5个:. 成员访问运算符* 成员指针访问运算符?: 条件运算符:: 域运算符sizeof

取长度运算符其中,前两个的功能是不允许改变的,而::和sizeof的运算对象是数据类型而不是变量或表达式。2.运算符重载不能改变运算符的优先级和结合性C++语言内部已经规定了所有运算符的优先级。例如,“!”的优先级高于“&&”。不论运算符作用于什么对象,该优先级都不能改变。另外,也不能通过重载,改变一个运算符的结合性。3.运算符重载不能改变运算符的操作数的个数单目运算符只能包含一个操作数,双目运算符必须有两个操作数,不能试图通过运算符重载改变操作数的个数。某些运算符,如-和*,既是单目运算符,也是双目运算符,一次重载只能使用其中一种含义。4.运算符重载不能使用默认参数与普通的函数重载不同,运算符重载时,不能使用默认的参数值,必须明确指出每一个操作数。5.运算符重载只能作用于自定义类型6.不能建立新的运算符7.不能改变运算符的含义8.运算符必须显式重载,不能隐式重载例如,重载了运算符+和运算符=后,语句obj3=obj1+obj2;是正确的,但是语句obj1+=obj2;是不正确的。要想使该语句成立,必须显式重载运算符+=。8.1.3运算符重载的方式运算符也是一种特殊的函数,为了能够操作对象的数据成员,在进行重载时主要采用两种方式:成员函数方式和友元函数方式。当作为成员函数进行重载时,它的左操作数必须是该成员函数所属类的一个对象,因为必须通过类的对象调用该类的成员函数。当左操作数是标准类型的变量或者是其他类的对象时,必须使用友元函数进行重载。当需要保留某些运算符的交换性时,必须使用友元函数进行重载。如果希望程序中能够同时接受下面的语句:obj1=1+obj2;和obj1=obj2+1;此时,必须使用友元函数进行重载。因为在使用成员函数进行重载时,左操作数必须为类的对象,不能为标准类型。通常,将单目运算符重载为成员函数,将双目运算符重载为友元函数。8.2双目运算符重载1.用成员函数重载双目运算符当使用成员函数重载双目运算符时,原型为:<类型>operator@(<形参>){

函数体;}其中,<类型>为运算符运算结果所属类型,operator是定义运算符重载函数的关键字,@为要重载的运算符。由于每个非静态成员函数都带有一个隐含的自引用参数this指针,形参表中的唯一的形参充当了双目运算符的右参数,左参数由调用该运算符的对象充当。【例8.1】定义复数类,并用成员函数重载运算符+。#include<iostream>usingnamespacestd;classMyComplex{doublereal,imag;public:

MyComplex(doublea,doubleb);

MyComplex();

MyComplex

operator+(MyComplex&c); //重载运算符+

MyComplex

operator+(intn); //重载运算符+voidprint(

);};MyComplex::MyComplex():real(0),imag(0){}MyComplex::MyComplex(doublea,doubleb):real(a),imag(b){}voidMyComplex::print(

){

cout<<"("<<real<<","<<imag<<")"<<endl;}MyComplex

MyComplex::operator+(MyComplex&c){returnMyComplex(this->real+c.real,this->imag+c.imag);}MyComplex

MyComplex::operator+(doublen){real+=n;return*this;}intmain(

){

MyComplexc1(2,-3),c2(5,6),c3;c3=c1+c2; //等价于c3=c1.operator(c2);c3.print(

);c1=c1+1; //等价于c1=c1.operator(1);c1.print(

);return0;}运行结果:(7,3)(3,-3)2.用友元函数重载双目运算符用友元函数重载运算符的原型为:friend<类型>operator@(<参数列表>);由于友元函数不是类的成员,没有this指针,因此对于二元运算符函数,需要声明两个形参。【例8.2】定义复数类,并用友元函数重载运算符+。#include<iostream>usingnamespacestd;classMyComplex{doublereal,imag;public:

MyComplex(doublea,doubleb);

MyComplex(

);voidprint(

);//以下三个函数为重载运算符+

friendMyComplex&operator+(MyComplex&c1,MyComplex&c2); friendMyComplex&operator+(MyComplex&c,doublen); friendMyComplex&operator+(doublen,MyComplex&c); };MyComplex::MyComplex(

):real(0),imag(0){}MyComplex::MyComplex(doublea,doubleb):real(a),imag(b){}voidMyComplex::print(

){

cout<<"("<<real<<","<<imag<<")"<<endl;}MyComplex&operator+(MyComplex&c1,MyComplex&c2){returnMyComplex(c1.real+c2.real,c1.imag+c2.imag);}MyComplex&operator+(MyComplex&c,doublen){

c.real+=n;returnc;}MyComplex&operator+(doublen,MyComplex&c){

c.real+=n;returnc;}intmain(

){

MyComplexc1(2,-3),c2(5,6),c3;c3=c1+c2; //等价于c3=operator(c1,c2);c3.print(

);c1=c1+1; //等价于c1=operator(c1,1);c1=1+c1; //等价于c1=operator(1,c1);c1.print(

);return0;}运行结果:(7,3)(4,-3)8.3单目运算符重载重载单目运算符的方法与重载双目运算符的方法是类似的,由于单目运算符只有一个操作数,该操作数必须是类的对象或对象的引用。1.用成员函数重载单目运算符当使用成员函数重载单目运算符时,原型为:<类型>operator@(

){

函数体;}由于每个非静态成员函数都带有一个隐含的自引用参数this指针,对于一元运算符函数,不能再显式声明形参,所需要的形参由自引用参数提供。【例8.3】用成员函数重载运算符,实现复数一元减法。#include<iostream>usingnamespacestd;classMyComplex{doublereal,imag;public:

MyComplex(doublea,doubleb);

MyComplex();voidprint(

);

MyComplexoperator-(

); //重载运算符-};MyComplex::MyComplex():real(0),imag(0){}MyComplex::MyComplex(doublea,doubleb):real(a),imag(b){}voidMyComplex::print(

){

cout<<"("<<real<<","<<imag<<")"<<endl;}MyComplex

MyComplex::operator-(

){returnMyComplex(-real,-imag);}intmain(

){

MyComplexc1(2,-3);c1=-c1;c1.print(

);

getchar(

);return0;}运行结果:(-2,3)2.用友元函数重载单目运算符【例8.4】用友元函数重载运算符,实现复数一元减法。#include<iostream>usingnamespacestd;classMyComplex{doublereal,imag;public:

MyComplex(doublea,doubleb);

MyComplex();voidprint(

);friendMyComplexoperator-(MyComplex&c); //重载运算符-};MyComplex::MyComplex():real(0),imag(0){}MyComplex::MyComplex(doublea,doubleb):real(a),imag(b){}voidMyComplex::print(

){

cout<<"("<<real<<","<<imag<<")"<<endl;}MyComplexoperator-(MyComplex&c){returnMyComplex(-c.real,-c.imag);}intmain(

){

MyComplexc1(2,-3);c1=-c1;c1.print(

);return0;}运行结果:(-2,3)8.4赋值运算符重载8.4.1赋值运算符重载与深复制在3.5节中曾经谈到“浅复制”和“深复制”问题。在例3.9中,类MyTest包含一个int型数据成员x和一个char*型数据成员s,考虑如下语句:MyTestc1(2,"Hello"),c2;c2=c1;在执行赋值语句时,系统会自动将c1.x赋值给c2.x,c1.s赋值给c2.s,如图8.1所示。由于c1.s和c2.s指向同一个存储单元,因此一旦c1和c2其中的任何一个被释放,都会影响到另外一个,这就是“浅复制”。通常,在进行复制时需要进行“深复制”,即令c1.s和c2.s指向不同的内存单元,这就需要对赋值运算符重载。图8.1浅复制8.4.2赋值运算符重载格式由于赋值运算符只能通过成员函数进行重载,而不能通过友元函数重载,因此在对赋值运算符重载时,应该具备如下结构:<类名>&<类名>::operator=(<形参obj>){if(this!=&obj){deletedobj;//释放引用者已经分配的动态存储空间(空间首址dobj)

//使用new为引用者分配与形参obj对象同样大小的动态存储空间

//将形参obj对象的动态存储空间中的数据赋给引用者对象的dobj成员

return*this; //返回引用者对象}【例8.5】改造例3.9,实现深复制。#include<string.h>#include<iostream>usingnamespacestd;classMyTest{

intx;char*s;public:

MyTest(intx1,char*s1);

MyTest(

){}~MyTest(

);

MyTest&operator=(MyTest&m);

MyTest&operator=(char*p);voidsetX(intx1);voidsetS(char*s1);voidprint(

);};MyTest::MyTest(intx1,char*s1){x=x1;s=newchar[strlen(s1)+1];

strcpy(s,s1);}MyTest::~MyTest(

){delete[]s;}MyTest&MyTest::operator=(MyTest&m){

if(this!=&m){

if(!s)delete[]s;x=m.x;s=newchar[strlen(m.s)+1];

strcpy(s,m.s);return*this;}}MyTest&MyTest::operator=(char*p){

if(!s)delete[]s;s=newchar[strlen(p)+1];

strcpy(s,p);return*this;}voidMyTest::setX(intx1){x=x1;}voidMyTest::setS(char*s1){

strcpy(s,s1);}voidMyTest::print(

){

cout<<x<<","<<s<<endl;}intmain(

){

MyTestc1(2,"Hello!"),c2(1,"Good!");c2=c1;c1.print(

);c2.print(

);c1.setX(-1);c2.setS("Good");c1.print(

);c2.print(

);c1="What?";c1.print(

);

getchar(

);return0;}运行结果:2,Hello!2,Hello!-1,Hello!2,Good!-1,What?8.4.3赋值运算符重载函数与复制初始化构造函数赋值运算符重载函数和复制初始化构造函数都是用来把一个类的对象复制到另一个同类型的对象。然而它们被调用的时机是不同的:(1)复制初始化构造函数是用已存在对象的各成员当前值来创建一个相同的新对象。在下述三种情况下,系统将自动调用所属类的复制初始化构造函数。①定义一个新的对象同时利用一个已有对象初始化的情况。②一个对象被作为函数参数的情况。③对象作为函数值进行返回的情况。(2)赋值运算符重载函数仅在赋值语句中被调用,用于把一个已存在对象的各成员的值赋值给另一个已存在的同类对象。例8.6】分析下面程序的输出结果。#include<string.h>#include<iostream>usingnamespacestd;classString{char*buffer;public:String(char*s);String(

){}

String(String&s);~String(

);String&operator=(String&m);String&operator=(char*p);voidprint(

);};String::String(char*s){buffer=newchar[strlen(s)+1];

strcpy(buffer,s);}String::String(String&s){buffer=newchar[strlen(s.buffer)+1];

strcpy(buffer,s.buffer);}String::~String(

){delete[]buffer;}String&String::operator=(String&m){

if(this!=&m){

if(!buffer)delete[]buffer;buffer=newchar[strlen(m.buffer)+1];

strcpy(buffer,m.buffer);return*this;}}String&String::operator=(char*p){

if(!buffer)delete[]buffer;buffer=newchar[strlen(p)+1];

strcpy(buffer,p);return*this;}voidString::print(

){

cout<<buffer<<endl;}intmain(

){Strings1("Hello!"),s2("Good!"),s3;s1=s2; //调用第一个重载赋值运算符函数

s3="What?"; //调用第二个重载赋值运算符函数

Strings4=s3; //调用复制初始化构造函数

s1.print(

);s2.print(

);s3.print(

);s4.print(

);return0;}运行结果:Good!Good!What?What?8.5几个典型运算符的重载8.5.1++和运算符重载++和运算符是单目运算符,它们又分为前缀形式和后缀形式两种,在重载时既可以用成员函数重载,也可以使用友元函数重载。用成员函数重载前缀++运算符的格式为: <类型>

<类名>::operator++();用成员函数重载后缀++运算符的格式为:

<类型>

<类名>::operator++(int);为了区分++运算符的前缀运算和后缀运算,需要将后缀运算重载为双目运算符。后缀运算符中的参数起到一个占位符的作用,在函数体内并不使用,因此通常不给出参数的名字。【例8.7】用成员函数形式重载运算符++和。#include<iostream>usingnamespacestd;classTime{

inthour;//时

intminute;//分

intsec;//秒public:Time(inth,intm,ints);Time(

){}Time&operator++(

); //前缀++运算

Time&operator++(int); //后缀++运算

Time&operator--(

); //前缀--运算

Time&operator--(int); //后缀--运算

voidshow(

);};Time::Time(inth,intm,ints){hour=h;minute=m;sec=s;}Time&Time::operator++(

){

if(++sec==60){ //满60秒进1分钟

sec=0;

if(++minute==60){ //满60分钟进1小时

minute=0;

if(++hour==24) //满24小时进1天

hour=0;}}return*this;}Time&Time::operator++(int){Timet=*this;

if(++sec==60){ //满60秒进1分钟

sec=0;

if(++minute==60){ //满60分钟进1小时

minute=0;

if(++hour==24) //满24小时进1天

hour=0;}}returnt; //返回自增前的时间值}Time&Time::operator--(

){if(--sec<0){ //不足0秒退到前1分钟

sec=59;if(--minute<0){ //不足0分退到前1小时

minute=59;if(--hour<0) //不足01小时推到前1天

hour=23;}}return*this;}Time&Time::operator--(int){Timet=*this;if(--sec<0){{ //不足0秒退到前1分钟

sec=59;if(--minute<0){ //不足0分退到前1小时

minute=59;if(--hour<0) //不足01小时推到前1天

hour=23;}}returnt; //返回自减前的时间值}voidTime::show(

){

cout<<hour<<":"<<minute<<":"<<sec<<endl;}intmain(

){Timet1(23,59,59),t2(17,0,0),t3;++t1;t3=t1++;t1.show(

);t3.show(

);--t2;t3=t2--;t2.show(

);t3.show(

);

getchar(

);return0;}运行结果:0:0:10:0:016:59:5816:59:59用友元函数重载前缀++运算符的格式为:

<类型>

<类名>::operator++(<类名>&);用友元函数重载后缀++运算符的格式为:

<类型>

<类名>::operator++(<类名>&,int);同样,需要将后缀运算重载为双目运算符,后缀运算符中的第2个参数也起到占位符的作用,在函数体内并不使用。8.5.2[]运算符重载对于一些容器类(如前面讲过的数组类、集合类和字符串类等),通常需要获得某个元素的值,这可以通过为类定义专门的成员函数实现,但更方便的做法是在容器类中重载下标运算符[]。[

]运算符只能重载为成员函数,而不能重载为友元函数。一旦在容器类中重载了[

]运算符,就可以将该类当做数组进行处理,可以使用下标方式来存取其中的元素。[

]运算符的重载格式通常为:<类型>

&operator[](int);该重载函数只能有一个参数用于表示下标。[

]运算符必须能够出现在一个赋值操作符的左、右两边。为了能在左边出现它的返回值且是一个左值,可以把返回类型指定为一个引用。【例8.8】重载运算符[]。#include<string.h>#include<iostream>usingnamespacestd;constintLIMIT=20;classMyArray{private:

intsize; //数组大小

int*array; //数组元素起始地址public:

MyArray(int=1);

int&operator[](intn);~MyArray(

);};MyArray::MyArray(inti){

if(i<0||i>LIMIT) { //数组越界检查

cout<<"数组越界"<<endl;exit(1);}size=i;array=newint[size];}int&MyArray::operator[](inti){

if(i<0||i>=size) { //下标越界检查

cout<<"下标越界"<<endl;exit(1);}returnarray[i];}MyArray::~MyArray(

){delete[]array;size=0;}intmain(

){

intindex;

MyArrayarray(5);

for(inti=0;i<5;i++)

array[i]=i;

cout<<"输入所要查看元素的个数(1~20):";

cin>>index;

for(inti=0;i<index;i++)

cout<<"array["<<i<<"]"<<"="<<array[i]<<endl;return0;}第1次运行结果:输入所要查看元素的个数(1~20):3↙array[0]=0array[1]=1array[2]=2第2次运行结果:输入所要查看元素的个数(1~20):6↙array[0]=0array[1]=1array[2]=2array[3]=3array[4]=4下标越界8.5.3(

)运算符重载在C++语言中,函数调用func(arg1,arg2,…);被解释为:func.operator(

)(arg1,arg2,…);与[

]运算符一样,(

)运算符也可以重载。由于它的参数不止一个,因此(

)运算符常常看成是对[]运算符的扩展。当需要用1个以上的对象检索一个对象时,可以使用(

)运算符。与[]一样,(

)运算符必须重载为成员函数。【例8.9】重载运算符(

)。#include<string.h>#include<iostream>usingnamespacestd;constintLIMIT=20;classMyArray{private:

intsize1; //行数

intsize2; //列数

int*array; //数组元素起始地址public:

MyArray(int=1,int=1);

int&operator(

)(inti,intj);~MyArray(

);};MyArray::MyArray(inti,intj){

if((i<0||i>LIMIT)||(j<0||j>LIMIT)){ //数组越界检查

cou

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论