再上一篇:12.5 指向类成员的指针
上一篇:第十三章 运算符重载
主页
下一篇:第十四章 输入/输出流类库
再下一篇:14.2 C++的基本流类体系
文章列表

13.2 几个特殊运算符的重载

《VC++程序设计基础》,讲述C++语言的语法和标准库,以及Visual C++ 函数库和MFC类库的使用,并附相关代码示例。

上一节中介绍了一般运算符的重载方法,也介绍了++和--算符的重载,为了区分++是前置还是后置运算,重载运算符的方法有所不同。对于有特殊要求的运算符的重载方法与一般运算符的重载方法也有所不同。这些特殊运算符除了++和--外,包括:下标运算符、函数调用运算符、成员存取运算符、new、delete运算符。本节分别讨论这些运算符的重载方法。13.2.1 ++和--算符

用成员函数重载这二个运算符的方法已在上一节中作了介绍,本节介绍用友元函数重载这二个运算符的方法,由于这二个运算符的重载方法完全类同,所以,以++为例说明之。

用友元函数来实现++运算符的重载时,前置++运算符重载的一般格式为:

operater ++ (ClassName &);

其中参数是要实现++运算的对象。后置++运算符重载的一般格式为:

operater ++ (ClassName &,int);

其中参数是要实现++运算的对象,而第二个参数除了用于区分是后置运算外,并没有其它的意义,因此第二个参数名可给出,也可不给出,其作用是相同的。

例 13.6 用友元函数实现++的重载。

#include

class ThreeD{

float x,y,z;

public:

ThreeD(float a=0,float b=0, float c=0){x=a;y=b;z=c;}

ThreeD operator +(ThreeD & t)

{

ThreeD temp;

temp.x= x+t.x;

temp.y=y+t.y;

temp.z=z+t.z;

return temp;

}

friend ThreeD & operator ++(ThreeD &);

friend ThreeD operator ++(ThreeD & ,int);

~ThreeD(){}

void Show(){cout << "x="<

};

ThreeD & operator ++(ThreeD & t)

{

t.x++;

t.y++;

t.z++;

return t;

}

ThreeD operator ++(ThreeD &t ,int )

{

ThreeD temp=t;

t.x++;

t.y++;

t.z++;

return temp;

}

void main(void )

{

ThreeD m1(25,50, 100),m2(1,2,3),m3;

m1.Show();

++m1;

m1.Show();

m2++;

m2.Show();

m3=++m1 + m2++;

m3.Show();

}

执行程序后的输出为:

x=25 y=50 z=100

x=26 y=51 z=101

x=2 y=3 z=4

x=29 y=55 z=106

程序中定义的类ThreeD是描述一个空间点的三维坐标,对对象的++运算要对该点坐标的三个分量(x,y,z)分别完成加1运算。

13.2.2 下标运算符

在 C++中使用数组元素时,系统并不作下标是否越界的检查,我们可以通过重载下标运算符来实现这种检查或完成更广义的操作。

重载下标运算符的一般格式为:

ClassName :: operator [ ]()

{

......;//函数体

}

其中type是函数返回值的类型,是参数,该参数指定了下标值。对于下标运算符的重载,要注意以下几点:

1、下标运算符只能由类的成员函数来实现,不能使用友元函数来实现。

2、左操作数必须是对象。

3、下标运算符重载函数只能是非静态的成员函数,有且仅有一个参数。

例 13.7 使用重载下标运算符来实现下标越界检查。

#include

#include

#include

class Array{

int len;

float *arp;

public:

Array(int n=0);

~Array()

{

if (arp ) delete [ ] arp;

}

int GetLen() const

{ return len; }

void SetLen(int l)

{

if(l>0) {

if (arp ) delete [ ] arp;

arp = new float[l];

memset(arp,0,sizeof(float) *l); //A

len =l;

}

}

float & operator [ ](int index);

};

Array:: Array(int n)

{

if(n>0){

arp = new float[n];

memset(arp,0,sizeof(float) *n);

len =n;

}

else {

len=0;

arp =0;

}

}

float & Array:: operator [](int index)

{

if ( index >=len || index < 0){

cout << "\nError:下标"<

exit(2);

}

return arp[index];

}

void main(void )

{

Array m1(10),m2(3);

int i;

for(i=0;i<10;i++) m1[i]=i;

for(i=1;i<11;i++) //B

cout << m1[i]<<'\t'; //C

cout << '\n';

m2[2]=26;

cout <<"m2[2]="<

}

程序中A行调用一个库函数,将所分配的存储空间均置为0。operator [ ]函数查看其下标是否出界,若不出界,则返回数组中相应元素的引用;否则输出出错信息,并终止程序的执行。这是示意性的出错处理方法,实际处理时,应根据具体的应用要求而作不同的处理。在C行,当要输出m1[10]值时,因下标出界而终止程序的执行。当把B行中的11改为10时程序能正确执行。

尽管下标运算符分为二部分:一个左方括号和一个右方括号,但系统认为是一个双目运算符,当表达式中出现运算符[]时,第一个操作数在[的左边,并且这操作数总是一个类的对象;而另一个操作数总是位于]的左边,这个操作数实际上可以是由operator [ ]所处理的任意类型,函数的返回值也可以是由operator [ ]函数处理后的任意类型数据。由于本函数只能有一个参数,故只能对一维数组进行下标重载处理。对于多维数组可有二种方法来处理,一种是将多维数组作为一维数组来处理;另一种方法是通过重载函数调用运算符来处理。

*例 13.8 先建立一张职工工资表,输出职工工资表。然后输入一个要修改工资的职工姓名,输入新的工资后,再一次输出工资表。

分析:可以定义描述一个人的基本信息的类Person,数据成员包括姓名,年龄和性别。再定义一个类 SalaryTab,它的一个成员指向职工基本信息表,一个成员指向职工工资表,一个成员指出表的长度,另一个成员指出表中实有的职工数。再增加相应的成员函数就可达到题目要求。程序为:

#include

#include

#include

class Person{ //定义描述一个职工基本信息的类

char *Name; //姓名

int Age; //年龄

char Sex; //性别,'0'为男,其它值为女

public:

Person(char *name,int age, char sex);

Person(Person & p);

Person( )

{Name =0; Age=0;Sex=0;}

~Person( )

{ if (Name ) delete [ ] Name; }

void SetName(char * name) //修改职工的名字

{

if (Name) delete [ ] Name;

Name = new char [strlen(name)+1];

strcpy(Name,name);

}

void SetAge(int age) { Age = age;} //修改职工的年龄

void SetSex(char sex) //修改职工的性别{

Sex=sex;

}

char *GetName() {return Name;} //取职工的名字

int GetAge() {return Age;}

char GetSex() {return Sex;}

Person & operator =(Person &p) //重载运算符=

{

if (Name) delete [ ] Name;

Name = new char[strlen(p.Name)+1];

strcpy(Name,p.Name);

Age=p.Age;

Sex=p.Sex;

return *this;

}

operator char *( ) {return Name;} //转换函数

operator char () {return Sex;} //转换函数

operator int() {return Age;} //转换函数

void Show();

};

Person:: Person(char *name,int age, char sex) //定义构造函数{

Name = new char [strlen(name)+1];

strcpy(Name,name);

Age=age;

Sex=sex;

}

Person:: Person(Person & p) //定义构造函数

{

if (p.Name ){

Name = new char [strlen(p.Name)+1];

strcpy(Name,p.Name);

Age=p.Age;

Sex=p.Sex;

}

else {

Name=0;

Age=0;

Sex=0;

}

}

void Person:: Show() //输出一个职工的基本信息{

cout <<"姓名:"<

<<"性别:"<<(Sex=='0'?"男":"女")<<'\n';

}

class SalaryTab{ //定义职工表类

Person *Staff; //指向职工表

float *Salary; //指向工资表

int nMax,nElem; //分别为表的长度和实际表目数public:

SalaryTab(int n=100) //缺省的表长为100

{

if (n>0){

Staff =new Person[n];

Salary = new float[n];

nMax =n;

nElem =0;

}

else {

Staff =0;

Salary=0;

nMax=0;

nElem=0;

}

}

float & operator [](Person &); //重载下标运算符

void PrintTab();

~SalaryTab() //析构函数

{

if (nMax){

delete [nMax] Staff;

delete [nMax] Salary;

}

}

};

void SalaryTab::PrintTab() //输出职工工资表

{

Person *p=Staff;

cout<<"序号\t姓名\t年龄\t性别\t工资\n";

for(int i=0;i

{

cout<

<<'\t'<<(((char )Staff[i]=='0')?"男":"女")<<'\t'<

p++;

}

}

float & SalaryTab::operator [](Person & per) //重载下标运算符

{

float l=-1;

for( int i=0;i

if(strcmp((char *)per,(char *) Staff[i])==0)

return Salary[i]; //职工表中找到了这个职工

if(nElem == nMax ) return (l); //职工表已满

Staff[i]=per; //把新职工加入职工表

Salary[i]=0;

nElem++;

return Salary[i];

}

void main(void )

{

SalaryTab tab;

Person per;

char name[80],sex;

int age;

float salary;

cout<<"输入数据:\n";

for (int j=0;j<3;j++) {

cout<<"输入姓名:";

cin.getline(name,80);

cout<<"输入年龄:";

cin>>age;

cout<<"输入性别:0为男,1为女:";

cin>>sex;

cout<<"输入工资:";

cin>>salary;

per.SetName(name);

per.SetAge(age);

per.SetSex(sex);

tab[per] = salary;

cin.getline(name,80);

}

cout<<"\n 工资表\n";

tab.PrintTab();

cout<<"输入要修改职工工资的姓名:";

cin.getline(name,80);

per.SetName(name);

cout<<"现在的工资="<

cout<<"输入新的工资:";

cin>>salary;

tab[per]=salary;

tab.PrintTab();

}

为了简化程序,在程序中并没有全部用到已定义的成员函数。程序中下标运算符重载函数的参数是对象,它的功能是:首先根据对象per中的职工姓名,查找职工表,若表中有这个职工,则返回该职工的工资;若职工表中没有这个职工且职工表不满时,则将这个职工加入到职工表中,并返回0;否则返-1。简言之,这函数的返回值大于0时,表示职工表中有这个职工;返回值为0时,表示将这个新职工已经加入职工表;返回值为-1时,表示职工表满,不能把新职工加入职工表。程序中其它函数的功能都比较简单,并且在程序中均作了功能说明,读者可自己分析并读懂程序。

从上例中可以看出,重载下标运算符的主要的意义在于扩大了数组下标的概念,这里的下标表达式(函数的参数)是一个类的对象。同样地,若把下标参数说明为一个指向字符串的指针时,而调用重载下标运算符时的实参可为一个文件名,通过这个文件名,就可以像使用普通变量那样对文件进行操作了。

13.2.3 函数调用运算符

在 C++中,把函数名后的括号()称为函数调用运算符。函数调用运算符也可以像其它运算符一样地进行重载。重载函数调用运算符的格式为:

ClassName::operater ( )()

{

......;//函数体

}

其中是参数表,与通常的函数一样,可以带有 0个或多个参数,但不能带有缺省的参数。

例 13.9 通过重载函数调用运算符,完成二维数组下标的合法性检查。

#include

#include

#include

const int cor1=10;

const int cor2=10;

class Array{

float arr[cor1][cor2];

public:

Array()

{

memset(arr,0,sizeof(float)*cor1*cor2); //数组元素均置为0

}

~Array(){}

void operator ()(int i,int j,float f);

float GetElem(int i,int j);

};

void Array:: operator ()(int i,int j,float f)

{

if( i>=0 && i=0 && j

arr[i][j]=f;

else{

cout <<"下标出界!\n";

exit(2);

}

}

float Array:: GetElem(int i,int j)

{

if( i<0 || j<0 || i>=cor1 || j >=cor2){

cout <<"下标出界!\n";

exit(3);

}

return arr[i][j];

}

void main(void )

{

Array a;

int i,j;

for(i=0;i<10;i++)

for(j=0;j<10;j++)

a(i,j,(float) i*j); //A

cout << '\n';

for(i=0;i<10;i++){

cout << '\n';

for(j=0;j<10;j++) {

cout <<"a["<

if ((j+1)% 5==0)cout <<'\n';

}

}

cout <<'\n';

}

在程序中,A行调用函数a(i,j,(float) i*j),是调用了下标运算符重载函数a. operator ( )(i,j,(float) i*j),它将i*j的值赋给对象a中的成员arr[i][j],并完成下标的合法性检查工作,若下标出界则终止程序的执行。B行中成员函数GetElem(i,j),获取对象a中的成员arr[i][j]的值。

下标运算符重载函数的左操作数一定是对象。对更多维数组的下标是否出界的检查方法是类同的。

*13.2.4 成员选择运算符

成员选择运算符->在C++中也允许重载,但这种重载通常并没有多少实用的意义,不作详细介绍了。

*13.2.5 new和delete运算符

new和delete运算符分别是用于动态申请存储空间和释放存储空间的,这二个运算符的使用尽管已经相当方便了,但对于一些特殊的应用场合,仍希望重载new和delete运算符,如希望将某一个对象存储在特定的存储空间中,并对删除对象的存储空间进行控制等等。

重载运算符new的一般格式为:

void *CalssName::operator new (size_t size, )

{

......;//函数体

}

其中返回值是一个任意类型的指针(void *),第一个参数的类型必须是size_t ,在C++中size_t 定义为:

type unsigned int size_t

即是一个无符号的整数类型,它表示要求分配存储空间的字节数,在头文件stddef.h,mem.h,alloc,h,stdlib.h中均有这种类型的定义,在重载new运算符时,可以包含这几个头文件中的任意一个。其余的参数是可有可无的,由实际应用的需要而定。通常,new重载后,它的功能仍是分配size个字节的存储空间,并返回所分配空间的首地址。当然,这种重载是通过一个成员函数来实现的,在函数体内可做任何事情。

重载运算符delete的一般格式为:

void CalssName::operator delete (void * p,)

{

......;//函数体

}

即该函数没有任何返回值,它至少要有一个参数,这个参数是一个任意类型的指针(void *),通常是用运算符 new返回的指针;第二个参数是任选取的,若有第二个参数,它的类型必须是size_t。delete重载后,它的功能仍是收回p所指向的存储空间。

有关这二个运算符的重载,说明以下三点:

1、重载new和delete的函数必须是成员函数,不能是友元函数。

2、重载new和delete的函数总是静态的成员函数。在operator new和operator delete的定义中,不管是否使用了关键字 static,编译程序总是将这二个重载的函数看成是静态的成员函数。

3、operator new和operator delete函数不能是虚函数。

例 13.10 重载 new和delete运算符。

#include

class C{

float x,y;

public:

void Show()

{

cout <<"x="<

}

void *operator new(size_t s) //A

{

void *p=new char[s]; //

cout <<"调用函数new(size_t s),分配空间为:"<

return p;

}

void * operator new(size_t s,float a,float b) //B

{

C *p =(C*) new char [s]; //调用C++中预定义的new

p->x=a;

p->y=b;

cout <<"调用函数new(size_t s,float a,float b),分配空间为:"<

return p;

}

void operator delete (void *p) //C

{

delete (p); //调用C++中预定义的delete

cout <<"调用函数delete(void *p)\n";

}

};

void main(void )

{

C *p1 ,*p2;

p1 = new C; //调用C::operator new(size_t )

p1->Show();

p2= new(10,50) C; //调用C::operator new(size_t,float ,float )

p2->Show();

delete p1; //调用C::operator delete (void *p)

delete p2; //调用C::operator delete (void *p)

p1= (C *) ::new C; //调用C++中预定义的new

p1->Show();

::delete p1; //调用C++中预定义的delete

}

执行程序后的输出以下7行:

调用函数new(size_t s),分配空间为:8

x=-4.31602e+008 y=-4.31602e+008

调用函数new(size_t s,float a,float b),分配空间为:8

x=10 y=50

调用函数delete(void *p)

调用函数delete(void *p)

x=-4.31602e+008 y=-4.31602e+008

其中第2行和第7行的输出值是随机的,因动态分配的存储空间没有赋初值。

从上例中可以看出,执行重载运算符new时,第一个参数是由系统自动提供的,它根据给定的类型,自动计算出该类型的字节数,并作为要分配的字节数。如例中的语句:

p1 = new C;

编译器先调用类C的缺省的构造函数,在缺省的构造函数中调用A行中定义的new重载函数:

this-> operator new(sizeof (C ) );

然后将返回值赋给p1。对于主函数中的语句:

p2= new(10,50) C;

先处理表达式new(10,50) C,同样地,要调用类C的缺省的构造函数,在缺省的构造函数中调用B行中定义的new重载函数:

this-> operator new(sizeof (sizeof (C ) ,10,50) ;

完成初始化后,将返回值赋给p2。

在执行主函数中的表达式delete p2时,调用C行中定义的重载运算符delete,并将p2作为函数的参数。

一旦在类中重载了这二个运算符,凡涉及到该类的动态内存分配和释放,系统优先调用重载后的运算符。若希望使用C++预定义的new和delete,则在其前面要加上作用域运算符“::”。

在重载运算符new和delete时,为了动态地分配和回收内存空间,仍要用到C++中预定义的new和delete,或者使用预定义函数calloc()动态分配存储空间和free()释放内存空间。

对于初学者,在没有完全理解如何重载这二个运算符之前,最好不要随便重载这二个运算符。一旦理解不正确,很容易出现运行错误。

最后需要说明的是,用友元函数实现的运算符重载,当然是不会被派生类继承。用类的成员函数定义的转换函数和重载的运算符绝大多数都可以由派生类继承,在派生类中定义的转换函数和重载运算符函数将隐藏基类中定义的这些函数。成员函数operator =()比较特殊,除了它不能被派生类继承外,也不能将它说明为虚函数。其它类运算符和转换函数可以说明为虚函数,这种重载运算符的虚函数在派生类中被重定义后,调用时和一般的虚函数一样产生多态性的行为。

赋值运算符operator =()不能被生类继承的原因是,派生类的赋值语义除包含有基类的赋值语义之外,还增加有新的赋值语义(如增加新的成员)。由于派生类的赋值语义与基类中定义的赋值语义不一样,所以派生类不能继承基类中的赋值运算符operator =()。 13.3 一个字符串类

前面介绍的运算符重载的例子都比较简单,我们把定义一个字符串类作为重载运算符的综合应用。在C++中,系统提供的字符串处理能力比较弱,都是通过字符处理函数来实现的,并且不能直接对字符串进行加法、减法,字符串的拼接,字符串之间的相互赋值等操作。通过应用C++提供的运算符重载机制,可以提供字符串的直接操作能力,使得字符串的操作与一般的数据一样方便。

在定义的字符串类中,重载运算符“=”,以实现直接字符串的赋值;重载运算符“+”,以实现二个字符串拼接;重载运算符“<”、“>”、“==”,以实现二个字符串之间的直接比较;同时也要定义一些转换函数,以便提供使用上的方便。当然,如果需要的话,还可以重载“+=”等运算符。

例 13.11 实现字符串直接操作的字符串类。

//Ex13_11.h

#include

#include

class String{

protected:

int Length; //字符串的长度

char *Sp; //指向字符串的指针

public:

String(){Sp=0;Length=0;} //缺省的构造函数

String(const String &); //以对象作为参数

String(const char *s) //以一个字符串常量作为参数

{

Length = strlen(s);

Sp=new char[Length+1];

strcpy(Sp,s);

}

~String()

{ if(Sp) delete [ ] Sp; }

const char * IsIn(const char ) const;

int IsSubStr(const char *) const;

void Show() //输出字符串

{ cout << Sp<<'\n'; }

int GetLen() {return Length;} //取字符串的长度

char *GetString(){return Sp;} //取字符串

operator const char * () const //转换函数

{ return (const char *) Sp; }

String & operator =(String &);

friend String operator+(const String &,const String &);

friend String operator-(const String &,const char *);

int operator < (const String &) const;

int operator > (const String &) const;

int operator == (const String &) const;

};

String::String(const String &s) //参数为对象的构造函数{

Length = s.Length;

if(s.Sp) {

Sp=new char [Length+1];

strcpy(Sp,s.Sp);

}

else Sp=0;

}

const char * String::IsIn(const char c) const //A 字符c是否在字符串中{

char *p= Sp;

while (*p)

if( *p++ == c) return --p; //在字符串中

return 0; //不在字符串中

}

int String::IsSubStr(const char *s) const

//s所指向的字符串是否为类中字符串的子串

{

if(strstr(Sp,s)) return 1; //B

else return 0;

}

String operator+(const String &s1,const String &s2)//二个字符串相拼接{

String t;

t.Length = s1.Length + s2.Length ;

t.Sp =new char [t.Length +1];

strcpy(t.Sp,s1.Sp);

strcat(t.Sp,s2.Sp);

return t;

}

String operator - (const String &s1,const char *s2) //C 见后面的说明{

String t;

char *p1=s1.Sp, *p2;

int i=0, len = strlen(s2);

if ( p2=strstr(s1.Sp,s2) ){

t.Length = s1.Length - len ;

t.Sp =new char [t.Length +1];

while ( p1 < p2)

t.Sp[i++]= *p1++;

p1+=len;

while (t.Sp[i++]=*p1++);

}

else {

t.Length = s1.Length;

t.Sp= new char [t.Length +1];

strcpy(t.Sp,s1.Sp);

}

return t;

}

int String::operator < (const String &s) const//实现小于比较{

if(strcmp(Sp,s.Sp) <0 ) return 1;

else return 0;

}

int String::operator > (const String &s) const

{

if(strcmp(Sp,s.Sp) > 0 ) return 1;

else return 0;

}

int String::operator == (const String &s) const

{

if(strcmp(Sp,s.Sp) == 0 ) return 1;

else return 0;

}

String & String::operator =(String &s)

{

if (Sp) delete [ ] Sp;

Length = s.Length;

if (s.Sp) {

Sp = new char [Length+1];

strcpy(Sp,s.Sp);

}

else Sp=0;

return *this;

}

//Ex13_11.cpp

void main(void )

{

String s1("C++程序设计 "),s2,s3("学生学习 ");

String s,s5;

char *str="students study C++ programming! ";

s1.Show();

s2=s1; //对象赋值

s2.Show();

s = s3 + s2; //字符串拼接

s.Show();

s5=s-s1; //从s中删除s1中的字符串

s5.Show();

String s6(str);

s6.Show();

}

执行程序后输出以下5行:

C++程序设计

C++程序设计

学生学习 C++程序设计

学生学习

students study C++ programming!

对于程序中功能简单的函数,在程序中已作了注解,不作进一步的说明。下面对较难理解的函数作必要的说明。

A行中的成员函数IsIn(const char c),判断字符c是否包含在对象中的字符串中,若包含这字符,则返回指向包含字符的指针;若不包含,则返回0(空指针)。

在B行中,用到了一个字符串操作函数strstr()。该函数的原型在头文件string.h的说明为:

char * strstr(const char *s1, const char *s2);

其功能是测试字符串s2是否是字符串s1的子串。若是,则返回字符串s2在s1中第一个字符的开始位置(字符型指针),否则返回一个空指针。

C行中函数operator - (String &s1, char *s2)重载了运算符“-”,若s2所指向的字符串包含在对象s1中的字符串中时,则从s1中的字符串中删除包含s2所指向的字符串,并将删除后的字符串构成新的对象作为返回值。如设s1中的字符串为“STRING is ABCD”,s2所指向的字符串为“ABC”,则运算后的字符串为“STRING is D”。若对象s1中不包含s2所指向的字符串,则运算的结果仍为s1,返回s1的拷贝t。

例 13.12 字符串操作演示程序。

//Ex13_12.cpp

#include "Ex13_11.h

class Str :public String {

public:

Str operator +(Str &s)

{

Str t;

t.Length = Length + s.Length ;

t.Sp =new char [t.Length +1];

strcpy(t.Sp,Sp);

strcat(t.Sp,s.Sp);

return t;

}

Str(char *s):String(s){ }

Str(Str &s):String(s){ }

Str( ) :String() { }

Str operator = (Str &s)

{

if(Sp) delete [ ] Sp;

Length = s.Length;

if (s.Sp) {

Sp = new char [Length+1];

strcpy(Sp,s.Sp);

}

else Sp=0;

return *this;

}

};

void main(void )

{

Str s1("C++程序设计 "),s2("学生学习 "),s;

Str s3(s2),s5;

s1.Show();

s5=s1; //对象赋值

s3.Show();

cout<<"s2="<

s = s3 + s1; //字符串拼接

s.Show();

cout<<"p="<

}

在头文件Ex13_11.h的类String中,虽然用成员函数重载了运算符“=”,由于在派生类中不能继承,在派生类Str中必须重载“=”。在类String中,用友元函数重载了“+”,不能被Str所继承,在Str中重载了“+”。A行中用到了从基类中继承过来的转换函数。 当对象参与运算时,C++编译器首先试看该运算符是否为用成员函数重载的运算符,若是则调用相应的函数来实现这种运算。若不成立,C++编译器试图将运算符作为友元运算符;若仍不成立,C++编译器试图用类中定义的转换函数将对象转换为其它类型的操作数进行运算。若仍不成立,则C++编译器将给出错误信息。

习题与思考13

1.什么是运算符重载?

2.用成员函数实现运算符重载与用友元函数实现运算符重载,在定义和使用上有什么不同?

3.C++中的所有运算符是否都可以重载?凡是能用成员函数重载的运算符是否均能用友元函数实现重载?

4.转换函数的作用是什么?

5.定义一个复数类,通过重载运算符:=,+=,-=,+,-,*,/,==,!=,直接实现二个复数之间的各种运算。编写一个完整的程序(包括测试各种运算符的程序部分)。6.定义一个学生类,数据成员包括:姓名,学号,C++、数学和物理的成绩。重载运算符“<<”和“>>”,实现学生类对象的直接输入和输出。增加转换函数,实现姓名和总成绩的转换。设计一个完整的程序,验证成员函数和重载运算符的正确性。

7.定义描述平面上一个点的类point,重载“++”和“--”,并区分这二种运算符的前置和后置运算。构成一个完整的程序。

8.定义一个指向字符串的指针数组,重载下标运算符,实现下标是否出界的检查。9.通过重载函数调用运算符,完成三维数组下标的合法性检查。三维数组的类型可为整型或实型。

10.完善字符串类,增加以下运算符的重载:+=,-=,==,!=。在主函数中侧重检查重载运算符的正确性。