您的位置:首页 > 运维架构 > Linux

Unix/Linux C++应用开发-C++运算符重载

2013-09-11 21:05 489 查看

9.1 C++运算符重载简介

针对语言基本类型提供的运算符,C++中允许自定义类型重载这些运算符操作。这使得自定义类型对象可以自如地使用内部运算符集参与表达式运算,类似内置基本类型一样。通过以自定义类型的中添加函数的方法,C++中同样也可以来实现类似内置基本类型的运算操作。但是相对于提供的运算符重载实现来讲,显得复杂而不直观。

针对内置基本类型,C++已经完整的提供了运算符的实现。C++中所提供丰富的运算符,在使用内置基本类型定义的对象或者变量上可以方便自如的操作。这在前面运算符章节已经详细讲述过。那么对于C++中自定义类型最重要的类类型来讲,则需要自定义重载运算符函数作为类方法成员。实现内置类型对于运算符的基本操作。与基本内置类型不同是,C++中自定义类型运算符操作是通过自定义实现的,而内置类型运算符操作由语言本身内置实现。

对于C++中类类型来讲,有些运算符不需要重载其对象就可以直接使用。如赋值运算符“=”、取地址运算符“&”等。类的赋值运算符重载在前一章已经介绍过,作为C++编译器默认提供的成员方法,该方法主要实现类数据成员赋值的操作。另外与赋值运算符类似,取地址运算符也由编译器默认提供,当然默认提供的运算符操作方法也可以由开发者自行定义重载实现。

9.2  C++运算符重载规则

C++中不是所有提供的运算符都可以在类中允许重载实现。下面可以通过一个简单表格来说明目前C++中基本运算符哪些能够重载实现,哪些不能够在类中重载实现。见表格9-1运算符重载说明。

表9-1  运算符重载说明表

运算符

说明

+   -  *  /

算术运算符,主要用于操作数加减乘除运算,允许重载实现

+=   -=  *=  /=

算术与赋值运算符复合使用,主要用于操作数加减乘除同时赋值运算,允许类中重载实现

%

取余运算符,使用两操作数余数作为运算结果,允许重载实现

%=

取余与赋值运算符复合使用,主要用于操作数取余后赋值,允许重载实现

++  --

自增与自减运算符,允许重载实现

<   >  <=  >=   ==  !=

基本关系比较运算符,允许重载实现

&&   ||  !

逻辑运算符,允许重载实现

^    &     |      ~

位运算符,允许重载实现

^=    &=    |=

位与赋值运算符复合使用,允许重载实现

>>    <<

流操作符,允许重载实现

=

赋值运算符,允许重载实现

->

成员访问操作符,允许重载实现

[ ]

下标操作符,允许重载实现

()

函数调用操作符,允许重载实现

new     delete

动态分配和销毁对象地址空间操作关键字

new[ ] delete[ ]

动态分配和销毁对象数组实例操作关键字

C++中可以在类中定义并重载的运算符,在上述表格中已经作了说明。其中,最显著的是现有的四个运算符,他们不能被重载使用,开发者在编程时需要注意区分。对于自定义实现现有的运算符重载操作,首先不应该改变其原有的表达意义,即自定义的重载运算符操作不要出现实现与运算符本身相悖的功能。

另外重载实现的运算符必须为内置提供的运算符,不允许重载实现其它运算符。同时重载运算符与内置的运算符操作使用方式应该完全相同,不应该改变原有运算符的操作优先级、结合性以及所实现的基本功能等。

重载运算符在软件编程中应该有选择的应用,除了增强程序功能以及增加自定义类型运算符操作的直观性以外,也需要考虑性能问题。开发者应根据具体需求来判定需要成员函数来实现还是重载运算符实现效率更高。通常情况下当自定义类型中内置基本运算符无法直接运用,使用普通成员函数实现比较困难情况下可以考虑使用运算符重载实现。

9.3  重载一元运算符

C++类运算符重载部分将会从最基本的一元运算符开始介绍。下面将会通过基本概念讲解并结合重载一元运算符实例让初学者初步理解一元运算符重载的应用。

9.3.1  重载一元运算符基本概念

一元运算符重载函数在程序中通常可以定义为无参数类成员函数,或者只带有一个参数的友元函数。该重载函数的参数可以是自定义类型的对象,或者对象的引用也可以为普通内置类型参数。重载运算符函数定义基本语法如下所示。

operator return_type operational_character();                               //作为成员函数重载,无显式参数

friend return_type operatoroperational_character(parameter) //作为友元函数重载,可以带有一个参数

从上述重载函数定义语法看来,一元运算符重载实现非常简单。在作为成员函数重载还是作为外部友元函数重载则需要根据实际需要情况而定。下面将会通过一个完整的封装字符串操作的实例来描述一元操作符重载在实际应用中的情况。

9.3.2  实例9-1  封装字符串类重载一元运算符

C++中字符串同时兼顾C风格的字符串操作,在前面章节就字符串部分已经详细讲述过。C语言中字符串主要利用字符数组与字符指针来操作实现,而C++中由于提供自定义类型。因此,允许开发者自行定义实现除了语言提供的基本操作数据类型以外新的类型操作。那么从字符串更加方便的操作角度来讲,采用封装手法来实现字符指针的字符串操作类型,这也是很多大型软件系统中常常实现的基本功能之一。其原因在于,虽然C++函数库中已经提供了string类型操作的封装实现,但是自定义的字符串类型操作也可以增加更多常用的功能方法进去。最终将自行封装实现的类作为组件方式存在,供软件系统中不同模块共同使用。这也是C++中面向对象思想编程的一个优势之处,即可以根据自行定义新的类型来实现或者封装更多的操作功能。

下面将通过逐步添加方法成员的方式,字符串封装实例将会贯穿整个运算符重载章节,最终形成一个可用的字符串操作类。本实例中主要实现字符串类基本操作以及其一元操作符重载实现。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0901.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0901

* 源文件chapter0901.cpp

* 字符串操作类重载一元操作符

*/

#include <iostream>

using namespace std;

 

class MyString                                                                            //定义字符串操作封装类MyString

{

         public:

                   MyString();                                                                //字符串操作类构造函数

                   ~MyString();                                                              //字符串操作类析构函数

                   MyString&operator=(const char * pStr);             //字符串赋值运算符重载函数

                   operatorchar *();                                                     //字符串解引用运算符重载函数

         public:

                   enum                                                                         //字符串操作类枚举成员,表示默认分配空间大小

                   {

                            MYSTRING_LEN= 20

                   };

         private:

                   char* m_string;                                                       //字符串操作类私有数据成员,字符型指针

                   int    m_stringLen;                                                //字符串操作类似有数据成员,表示字符串长度

};

MyString::MyString()                                                                  //字符串操作类构造函数定义

{

         m_stringLen= MYSTRING_LEN;                                  //初始化字符串操作类中字符串长度数据成员

         m_string  = new char[m_stringLen+1];                       //采用动态内存为其字符指针成员分配内存空间

         assert(m_string!=NULL);                                                 //断言判断该字符指针成员分配内存是否成功

         memset(m_string,0,m_stringLen+1);                                    //初始化设置分配的堆内存值

}

MyString::~MyString()                                                                //字符串操作类析构函数定义

{

         if(m_string!=NULL)                                                          //当字符串操作类中分配内存依然存在时

         {

                   delete[]m_string;                                                     //删除该内存

                   m_string  = NULL;                                                 //同时字符指针指向NULL

                   m_stringLen= 0;                                                     //字符串长度也设置为0

         }

}

MyString& MyString::operator=(const char *str)                  //字符串操作类赋值运算符重载函数定义

{

         if(m_stringLen< strlen(str))                                            //判断传入字符串大小是否比默认分配的大

         {

                   if(m_string!=NULL)delete[] m_string;                 //如果比默认大则需要作重新分配以容纳字符串

                   m_stringLen= strlen(str);                                       //首先清除掉已经分配的内存空间,根据传入字符串大小

                                                                                                       //重置字符串操作类中表示大小的数据成员

                   m_string  = new char[m_stringLen+1];             //重新分配m_stringLen+1大小的内存空间

         }

         strcpy(m_string,str);                                                          //将传入的字符串拷贝至内存空间中

         return*this;                                                                         //返回类的this指针值

}

MyString::operator char *()                                                       //字符串操作类解引用运算符重载函数定义

{

         returnm_string;                                                                 //直接返回其数据成员字符指针

}

int main()

{

         MyStringmystring;                                                            //定义字符串操作类MyString对象实例

         mystring= "jack";                                                              //调用赋值重载运算符为其对象实例赋值

         cout<<"mystringvalue:"<<mystring<<endl;                 //打印输出该字符串操作类对象

         cout<<"*mystringvalue:"<<*mystring<<endl;             //打印运用解引用运算符的字符串操作类对象实例

         return0;

}

本实例程序主要通过重载字符串类中等于号“=”与指针解引用操作符“*”,演示在类中实现一元符号重载操作的功能。程序主要包含主函数与字符串类两个部分,其中字符串类中除了相应的构造析构方法定义外,还包含了重载等于号与解引用操作符的方法。具体程序剖析见程序注释与后面的程序讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0901.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0901.o

CC=g++

 

chapter0901: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0901

clean:

    rm -fchapter0901 core $(OBJECTS)

submit:

    cp -f -rchapter0901 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0901.o chapter0901.cpp

g++ chapter0901.o -g -o chapter0901

[developer @localhost src]$ make submit

cp -f -r chapter0901 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ chapter0901

mystring value:jack

*mystring value:j

4.程序剖析

本实例程序主要用于实现基本字符串类类型操作,字符串类MyString中主要涉及如下方法成员,构造函数MyString、析构函数~MyString、等于号赋值运算符重载MyString&
operator=(const char * pStr)以及指针解引用运算符重载operator char *()。另外还包含如下的数据成员,一个公开数据成员为枚举类型。

由于类中不能直接使用const常量定义,但是类中涉及常量的使用也可以采用枚举类型来代替。数据成员还包括两个私有类型,字符指针类型变量m_string表示字符串,整型变量m_stringLen表示字符串长度。方法成员中解引用运算符“*”为一元运算符重载实现,下面将会详细分析类的各个组成成员。

首先分析本实例中的MyString类构造函数。构造函数中主要用于初始化该类的数据成员。由于字符串定义需求,其长度为不固定,因为无法事先获知字符串具体大小。所以构造函数中为字符指针m_string分配空间时,采用动态堆内存方式。

主程序中定义MyString类对象实例。首先调用该类的构造函数,它会根据事先设定的默认大小,为其字符指针成员分配、获取堆内存空间。构造函数中,首先,其数据成员m_stringLen被使用时预先设定枚举值的初始化。随后数据成员m_string指向新分配的m_stringLen+1大小的堆内存首地址,最后需要将m_string指向的内存空间采用memset库函数全部初始化为0。

主程序中MyString类对象实例定义完毕后,调用其赋值运算符重载方法,将字符串常量“jack”赋值给新定义对象实例mystring。其中操作符“=”重载函数中根据传入的常量字符串实参,首先需要判断其大小,因为预先分配的堆内存空间大小有限。一旦如果传入的字符串大于预先分配的长度,则需要另行扩充。

代码中if(m_stringLen< strlen(str))条件成立,则随后删除已有分配的空间,采用strlen方法计算传入字符串的大小重新分配。如果,在预定范围内则直接采用strcpy方法,执行该字符串拷贝到内存空间,内存空间是该字符指针成员指向的内存空间。最后返回该类的this指针解引用,供应用程序使用。

赋值运算符使用完以后,在主程序中打印该字符对象所指向的值。直接调用cout输出流对象打印输出mystring值,最后打印使用解引用运算符的mystring对象值。由于类中重载实现了解引用“*”符号操作,则在打印前首先执行其解引用重载操作。解引用重载操作中非常简单主要直接返回其数据成员m_string指针。由于m_string为字符指针成员所以应用解引用运算符后最终打印的为字符串首个字符“j”。

至此本实例实现了字符串类最一般的类型定义功能。在其基础之上添加了一元操作符“*”解引用运算符重载方法供程序使用。解引用运算符作为一元运算符重载,在程序中定义为无参数成员方法。初学者可以参照本实例实现方式自行添加其它一元运算符重载功能,加强对操作符重载实现的认识。

9.4  重载二元运算符

本小节将会继续沿用字符串封装实例来讲述二元运算符重载在其中的具体应用情况。二元运算符重载与一元运算符同样有着重载为成员函数还是友元函数以及针对两种不同的重载方式参数处理的不同。下面将会分析二元运算符在实际应用中使用情况。

9.4.1  重载二元运算符基本概念

类中二元运算符重载在程序中通常可以定义为,包含一个参数处理的成员函数,或者包含两个参数的友元函数。其中参数必须至少包含一个自定义类型对象以及对象引用。二元运算符重载基本语法与一元类似,只是运算符重载函数中的参数个数稍有变化,其基本定义语法如下所示。

return_type operatoroperational_character(parameter);           //包含单个参数的二元运算符重载成员函数

friend return_type operatoroperational_character(parameter1,parameter2);//包含两个参数二元运算符友元重载

二元运算符重载同样包含重载为类成员函数与友元函数两种形式。采用哪种方式确定为运算符重载定义视看重的方面不同而定。如果从封装性来讲不建议过多使用友元函数。如果从方便操作的角度来讲,建议使用友元函数来表示二元运算符重载。

9.4.2  实例9-2封装字符串类重载二元运算符

继续9-1字符串封装实例,继续添加“+”、“==”以及“!=”大小比较等二元运算符重载实现,其中二元运算符采用成员函数以及友元函数两种方式实现。比较两种方式在二元运算符重载使用时的便利情况。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0902.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0902

* 源文件chapter0902.cpp

* 字符串操作类重载二元运算符方法

*/

#include <iostream>

using namespace std;

 

class MyString                                                                                                                           //字符串操作类定义

{

         public:

                   MyString();                                                                                                              //构造函数

                   ~MyString();                                                                                                            //析构函数

                   MyString&operator=(const char *str);                                                                //重载等号操作函数

                   MyString&operator=(const MyString& leftString);                                           //新增对象方式赋值重载函数

                   MyString  operator+(const char *str);                                                                //新增字符串连接符重载函数

                   operatorchar *();

                   friendMyString operator+(const char* str, const MyString& rightString);    //重载+运算符友元函数

                   friendbool operator==(const MyString& leftString, const MyString&rightString);//友元重载==运算符

                   friendbool operator==(const MyString& leftString, const char * pStr);                           //同上

                   friendbool operator==(const char * pStr,const MyString& rightString);                //同上

                   friendbool operator!=(const MyString& leftString, const MyString&rightString);//友元重载!=运算符

                   friendbool operator!=(const MyString& leftString, const char * pStr);                            //同上

                   friendbool operator<(const MyString& leftString, const MyString&rightString);          //友元重载<运算符

                   friendbool operator<(const MyString& leftString, const char * pStr);                    //同上

                   friendbool operator<(const char *   pStr,const MyString& rightString);        //同上

                   friendbool operator>(const MyString& leftString, const MyString&rightString);          //友元重载>运算符

                   friendbool operator>(const MyString& leftString, const char * pStr);                    //同上

                   friendbool operator>(const char *   pStr,const MyString& rightString);            //同上

         public:

                   enum

                   {

                            MYSTRING_LEN= 30

                   };

         private:

                   char* m_string;

                   int    m_stringLen;

};

MyString::MyString()                                                                           //字符串操作类构造函数定义

{

         m_stringLen= MYSTRING_LEN;

         m_string  = new char[m_stringLen+1];

         memset(m_string,0,m_stringLen+1);

}

MyString::~MyString()                                                                         //字符串操作类析构函数定义

{

         if(m_string!=NULL)

         {

                   delete[]m_string;

                   m_string  = NULL;

                   m_stringLen= 0;

         }

}

MyString& MyString::operator=(const char *str)                           //字符串=赋值运算符重载函数定义

{

         if(m_stringLen< strlen(str))

         {

                   if(m_string!=NULL)delete[] m_string;

                   m_stringLen= strlen(str);

                   m_string  = new char[m_stringLen+1];

         }

         strcpy(m_string,str);

         return*this;

}

MyString& MyString::operator=(constMyString& leftString)       //新增字符串类对象赋值重载函数定义

{

         if(m_stringLen< strlen(leftString.m_string))                        //如果新字符串比当前默认容量大则重新分配内存

         {

                   if(m_string!=NULL)delete[] m_string;

                   m_stringLen= strlen(leftString.m_string);

                   m_string  = new char[m_stringLen+1];

         }

         strcpy(m_string,leftString.m_string);                                      //拷贝字符串对象中字符串指针给当前对象中成员

         return*this;

}

MyString MyString::operator+(const char *str)                               //新增字符串连接符重载函数定义

{

         MyStringmystring;                                                                     //定义字符串操作对象

         if(mystring.m_stringLen< strlen(m_string)+strlen(str))      //如果连接后的字符串大于当前类默认分配空间

         {

                   if(mystring.m_string!=NULL)delete[] mystring.m_string;  //则以下根据连接后的长度重新分配内存空间

                   mystring.m_stringLen= strlen(m_string)+strlen(str);

                   mystring.m_string  = new char[mystring.m_stringLen+1];

         }

         sprintf(mystring.m_string,"%s%s",m_string,str);                           //将字符串对象与字符串常量连接放入当前对象

         returnmystring;                                                                                    //返回连接后存放字符串的对象

}

MyString::operator char *()

{

         returnm_string;

}

MyString operator+(const char* str, constMyString& rightString         )                 //新增字符串连接符重载友元函数定义

{

         MyStringmystring;                                                                                        //定义字符串操作类对象

         if(mystring.m_stringLen< strlen(rightString.m_string)+strlen(str))     //如果当前类对象中字符串

                                                                                                                                   //比字符串相加的长度小

         {

                   if(mystring.m_string!=NULL)delete[] mystring.m_string;           //则重新分配适合大小的内存空间

                   mystring.m_stringLen= strlen(rightString.m_string)+strlen(str);

                   mystring.m_string  = new char[mystring.m_stringLen+1];

         }

         sprintf(mystring.m_string,"%s%s",str,rightString.m_string);                 //最后格式化连接字符串到对象字符串成员中

         returnmystring;                                                                                             //返回该字符串操作类对象实例

}

bool operator==(const MyString& leftString,const MyString& rightString) //新增==运算符重载友元函数定义

{

         return(strcmp(leftString.m_string,rightString.m_string)==0);              //直接返回strcmp函数与0的比较结果

}

bool operator==(const MyString& leftString,const char * str)                        //同上

{

         return(strcmp(leftString.m_string,str)==0);

}

bool operator==(const char * str,constMyString& rightString)                      //同上

{

         return(strcmp(rightString.m_string,str)==0);

}

bool operator!=(const MyString& leftString,const MyString& rightString)   //新增!=运算符友元函数定义

{

         return(strcmp(leftString.m_string,rightString.m_string)!=0);               //直接返回strcmp函数与0比较结果

}

bool operator!=(const MyString& leftString,const char * str)                         //同上

{

         return(strcmp(leftString.m_string,str)!=0);

}

bool operator<(const MyString& leftString,const MyString& rightString)    //新增<运算符友元函数定义

{

         return(strcmp(leftString.m_string,rightString.m_string)<0);                          //直接返回strcmp函数与0比较结果

}

bool operator<(const MyString& leftString,const char * str)                          //同上

{

         return(strcmp(leftString.m_string,str)<0);

}

bool operator<(const char * str, constMyString& rightString)                        //同上

{

         return(strcmp(str,rightString.m_string)<0);

}

bool operator>(const MyString& leftString,const MyString& rightString)    //新增>运算符友元函数定义

{

         return(strcmp(leftString.m_string,rightString.m_string)>0);                          //直接返回strcmp函数与0比较结果

}

bool operator>(const MyString& leftString,const char * str)                          //同上

{

         return(strcmp(leftString.m_string,str)>0);

}

bool operator>(const char * str, constMyString& rightString)                        //同上

{

         return(strcmp(str,rightString.m_string)>0);

}

int main()

{

         MyStringmystring1,mystring2;                                      //定义字符串操作类两个对象实例

         mystring1= "jack";                                                            //调用赋值操作符为mystring1赋值

         mystring2= "jack";                                                            //调用赋值操作符为mystring2赋值

         cout<<"mystring1and mystring2 value:"<<mystring1<<""<<mystring2<<endl;//打印两个对象值

         cout<<"*mystringvalue:"<<*mystring1<<endl;           //打印解引用操作符引用mystring1对象值

         mystring1= mystring1+"hello";                                      //调用连接操作符为mystring1对象与字符串常量连接

                                                                                                       //随后通过对象赋值运算符赋给对象mystring1

         cout<<"mystring1operatior+ \"hello\" value:"<<mystring1<<endl;//打印字符串连接后的mystring1对象值

         if(mystring1== mystring2)                                                       //调用==运算符重载函数,比较两个字符串操作类对象

         {

                   cout<<"mystring1is equal mystring2!"<<endl;  //如果两个对象值相等则打印该提示信息

         }

         if(mystring1== "john")                                                     //调用==运算符重载函数,比较对象值与字符串常量

         {

                   cout<<"mystring1is equal \"john\"!"<<endl;       //如果相等则打印该提示信息

         }

         else

         {

                   cout<<"mystring1is not equal \"john\"!"<<endl; //不等则打印此提示信息

         }

         if(mystring1!= mystring2)                                                        //调用!=运算符重载函数,比较两个字符串类对象

         {

                   if(mystring1> mystring2)                                       //如果两个对象不相等,则调用>运算符重载函数

                   {

                            cout<<"mystring1is large than mystring2!"<<endl;   //如果对比条件成立则打印此信息

                   }

                   elseif(mystring1 < mystring2)                                                 //否则调用<运算符重载函数

                   {

                            cout<<"mystring1is less than mystring2!"<<endl;     //如果对比条件成立则打印提示信息

                   }

         }

         elseif(mystring1 == mystring2)                                              //最后如果!=比较不成立则调用==运算符重载函数

         {

                   cout<<"mystring1is equal mystring2!"<<endl;  //如果对比条件成立则打印此提示信息

         }

         return0;

}

本实例主要通过类成员方法与友元方式演示类内部重载二元字符串操作的功能。程序主要由主函数与字符串操作类组成,其中字符串操作类中包含了比较“==”、“>”、“<”以及“!=”运算符重载的方法。具体程序剖析见程序注释与后面详细讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0902.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0902.o

CC=g++

 

Chapter0902: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0902

clean:

    rm -fchapter0902 core $(OBJECTS)

submit:

    cp -f -rchapter0902 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0902.o chapter0902.cpp

g++ chapter0902.o -g -o chapter0902

[developer @localhost src]$ make submit

cp -f -r chapter0902 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0902

mystring1 and mystring2 value:jack jack

*mystring value:j

mystring1 operatior+ "hello"value:jackhello

mystring1 is not equal "john"!

mystring1 is large than mystring2!

本实例中主要添加“+”、“==”以及“!=”、“<”与“>”重载实现函数,其中连接运算符“+”采用定义为字符串类成员函数和字符串类友元函数两种重载方式来实现。两种方式区别仅仅在于参数个数,其中友元函数定义中至少有一个字符串类对象或对象应用才使得重载操作有意义。

友元方式的连接符“+”重载函数定义中,根据传入的字符串常量以及字符串类对象,在函数内部,通过sprintf库函数调用,来实现字符串类对象中的字符串值与字符串常量的连接功能。

字符串操作类中的“==”运算符,根据传入的参数类型不同,而定义三种方式的“==”运算符重载。其区别分别为,两个字符串类对象作为参数的重载定义,则表示两个字符串类对象实例之间“==”比较时调用的重载函数。而第一个参数为常量字符串,第二个参数为字符串类对象的重载定义,则表示字符串常量与字符串类对象实例的“==”比较时调用的重载函数。

随后定义的则与第二个正好相反,表示字符串类对象与字符串常量“==”比较时调用的重载函数。该重载函数内部定义,通过strcmp库函数比较传入的参数结果再与0比较,返回其bool类型值。

字符串操作类中“!=”运算符,根据传入参数类型的不同定义两种方式的重载友元函数。两个重载函数的区别原因同运算符“==”,仅仅是根据参数传入的类型或者个数决定“!=”运算符比操作定义选择其中哪一个方法执行。“!=”运算符重载函数定义内部也是通过库函数strcmp比较,传入参数结果再与0比较,返回其bool型值供判断使用。

另外“<”与“>”运算符重载分别定义了三种不同应用方式,原因同前面两种运算符。本实例主程序中主要为重载运算符的应用测试。首先定义两个字符串操作类对象实例mystring1与mystring2,调用其对应的构造函数为其字符串指针成员分配动态内存空间。调用赋值运算符重载函数使用常量字符串,为两个字符串类对象赋值,两个对象实例分别赋值,为字符串常量“jack”。随后在屏幕输出两个对象实例中存放的字符串值,同样此处也调用解引用运算符重载函数,打印解引用对象mystring1的结果。

之后字符串操作类对象mystring1,首先调用连接运算符“+”,传入当前mystring1对象与字符串常量“hello”为实参调用其重载函数。然后,将其连接返回的对象,通过对象赋值运算符重载操作,重新赋值为mystring1对象,随后打印该对象中字符串值。最后通过一组if控制结构语句中对象mystring1与mystring2比较结果,执行不同的分支打印不同的信息。由于直接是对象间的比较,则重载运算符调用时都会调用对应的两个对象引用作为参数的重载函数实现比较操作。

9.5  重载++与--运算符

C++中“++”与“--”运算符通常表现为两种形式,即前缀增、减或后缀增、减方式。类中重载两类单目运算符时同样需要注意该类运算符的两种表示形式。为了区别两个运算符的不同表达形式在类中的重载实现,编译器通常会作出相应的规定。对于后缀式的表达方式运算通常需要增加一个临时变量来表达,下面将会详细讲述类中重载两类自增运算符的应用情况。

9.5.1  重载++运算符

C++自定义类型中++运算符的重载实现方式同样有两种表现形式,即表示前缀自增与后缀自增。C++中对于前缀与后缀的自增运算符号其语法实现形式不同,如下所示。

object_type operator++()                                                          //类成员函数重载实现前缀++

friend object_type operator++(object_type&)                       //类友元函数重载实现前缀++

object_type operator++(int)                                                      //类成员函数重载实现后缀++

friend object_type operator++( object_type&,int)                //类友元函数重载实现后缀++

从上述语法定义形式可以看出,由于++运算符作为前缀运算时可以直接作为一元运算符来重载实现。而作为后缀运算符时,则需要增加一个整型的参数用来作为运算区别。该参数不需要显式的使用,其初始值通常为0。下面将会通过一个完整的重载++运算符前后缀操作的实例,了解++运算符重载使用情况,该实例主要在计数类Count中实现前缀以及后缀++运算符重载操作。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0903.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0903

* 源文件chapter0903.cpp

* 重载++运算符方法

*/

#include <iostream>

using namespace std;

 

class Count                                                                        //计数类Count定义

{

         public:

                   Count();                                                            //计数类构造函数

                   ~Count(){}                                                                 //计数类析构函数

                   Count&operator++();                                    //重载前缀++运算符函数

                   Countoperator++(int);                                   //重载后缀++运算符函数

                   Countoperator+(int count);                          //重载+运算符函数

                   voidshowCount();                                          //打印计数类数据成员函数

         private:

                   intm_count;                                                    //私有整型计数成员

};

Count::Count()                                                                   //计数类构造函数定义

{

         m_count= 0;                                                            //初始化计数类计数数据成员

}

Count &Count::operator++()                                           //重载前缀++运算符函数定义

{

         *this= *this+1;                                                          //调用+运算符操作更新当前this对象指针值

         return*this;                                                               //返回更新后的当前对象值

}

Count Count::operator++(int)                                         //重载后缀++运算符函数定义

{

         Countcount = *this;                                                 //定义临时计数变量,保存当前对象的计数值

         *this= *this+1;                                                          //当前对象this指针值更新

         returncount;                                                             //返回保存的对象

}

Count Count::operator+(int value)                                 //重载+运算符函数定义

{

         Countcount = *this;                                                 //定义临时计数变量,保存当前对象值

         value+= count.m_count;                                       //根据传入的参数值加上当前计数值存入value中

         count.m_count= value;                                          //更新当前对象计数值

         returncount;                                                             //返回保存更新对象值后的对象

}       

void Count::showCount()                                                //打印计数类数据成员函数定义

{

         cout<<"countvalue:"<<m_count<<endl;

}

int main()

{

         Countcount;                                                             //定义计数类对象count

         ++count;                                                                    //调用重载前缀++运算符实现计数类对象前缀++运算

         count.showCount();                                                 //打印前缀++运算后的对象计数值

         count++;                                                                    //调用重载后缀++运算符实现计数类对象后缀++运算

         count.showCount();                                                 //打印后缀++运算后的对象计数值

         return0;

}

本实例主要通过重载++运算符操作,演示运算符++作为前后缀符号实现的区别。程序主要包含主函数与计数类两个部分,其中计数类中包含了重载的++运算操作符的方法。具体程序剖析见程序注释与后面讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0903.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0903.o

CC=g++

 

chapter0903: $(OBJECTS)

         $(CC)$(OBJECTS) -g -o chapter0903

clean:

         rm-f chapter0903 core $(OBJECTS)

submit:

         cp-f -r chapter0903 ../bin

         cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行该可执行程序Count,该程序运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0903.o chapter0903.cpp

g++ chapter0903.o -g -o chapter0903

[developer @localhost src]$ make submit

cp -f -r chapter0903 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0903

count value:1

count value:2

本实例中主要定义计数类Count,最主要的是实现计数类中对象的++运算符重载操作。其中++运算符主要采用计数类成员函数重载方式来实现前缀与后缀操作。友元函数的定义实现,初学者可以自行定义实现。类Count中主要包括构造函数、析构函数、重载实现的++运算符前缀后缀函数以及重载实现的“+”运算符的方法成员,数据成员主要包括整型计数变量m_count。

计数类Count构造函数中主要用于初始化数据成员m_count,析构函数由于其数据成员不涉及动态内存分配,因此不需要显式定义为空。类中成员函数首先是重载实现++前缀运算成员,该成员方法体内主要通过this指针的解引用加1操作实现计数前缀自增运算。其中*this
= *this + 1表达式计算需要使用重载的赋值运算符操作。该函数中主要根据表达式中右边的整型数作为实参传入,用于更新Count对象中的m_count值来实现对象的前缀++操作。

计数类的++后缀运算操作则通过定义临时的Count类对象,更新其值后返回对象实例供计算使用,以此实现++后缀运算符的功能。主程序中首先定义Count类对象实例count,随后调用重载前缀++运算符函数执行前缀++运算。随后调用showCount函数打印前缀++运算符计算后的对象成员值。同样后缀++运算执行过程与上述类似,但是后缀操作首先保留了当前对象的值,之后才计算自增值。

9.5.2  重载--运算符

C++中重载实现“--”运算符号方式与“++”运算符重载相同,其语法定义如下所示。

object_type operator--()                                                            //类成员函数重载实现前缀--

friend object_type operator--(object_type&)                         //类友元函数重载实现前缀--

object_type operator--(int)                                                                 //类成员函数重载实现后缀--

friend object_type operator-- ( object_type&,int)                          //类友元函数重载实现后缀--

同样“--”运算符的重载也分前缀与后缀运算的实现方式,下面依然通过计数类的重载前后缀“--”运算符实例来了解“--”运算符重载使用情况。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0904.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0904

* 源文件chapter0904.cpp

* 计数类内部重载--运算符操作

*/

#include <iostream>

using namespace std;

 

class Count                                                                                 //计数类声明

{

         public:                                                                                 //公共成员

                   Count();                                                                     //计数类构造函数

                   ~Count(){}                                                                          //计数类析构函数

                   Countoperator-(int count);                                    //计数类-号重载操作

                   Count&operator--();                                               //技术类前缀--符号重载操作

                   Countoperator--(int);                                              //计数类后缀--符号重载操作

                   voidshowCount();                                                   //打印输出计数信息

         private:                                                                                //私有成员

                   intm_count;                                                              //计数类计数变量

};

Count::Count()                                                                            //计数类构造函数定义

{

         m_count= 0;                                                                      //初始化计数类数据成员

}

Count &Count::operator--()                                                      //计数类前缀--符号重载操作定义

{

         *this= *this-1;                                                                    //先计算减操作

         return*this;                                                                         //返回减计算后的值

}

Count Count::operator--(int)                                                     //计数类后缀--符号重载操作定义

{

         Countcount = *this;                                                          //先获取计数变量值

         *this= *this-1;                                                                    //后计算减操作

         returncount;                                                                       //直接返回没减操作前的变量值

}

Count Count::operator-(int value)                                           //计数类减法操作符重载定义

{

         Countcount = *this;                                                          //定义计数对象获取初始值

         count.m_count-= value;                                                 //与传入的参数进行减法计算

         returncount;                                                                       //返回减法后的计算结果

}

void Count::showCount()                                                          //打印输出计数类计数变量的信息

{

         cout<<"countvalue:"<<m_count<<endl;

}

int main()

{

         Countcount;                                                                      //定义计数类对象count

         --count;                                                                                //执行前缀--操作

         count.showCount();                                                          //打印输出计数类数据信息

         count--;                                                                               //执行后缀--操作

         count.showCount();                                                          //打印输出计数类数据信息

         return0;

}

本实例主要通过重载计数类“--”操作符方式演示基本运算符在类中重载的一般方法。程序主要包含主函数与计数类两个部分,其中计数类中重载了“--”运算符前后缀计算操作以及计数类减法运算符操作。具体程序剖析见程序注释与后面的讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0904.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0904.o

CC=g++

 

chapter0904: $(OBJECTS)

         $(CC)$(OBJECTS) -g -o chapter0904

clean:

         rm-f chapter0904 core $(OBJECTS)

submit:

         cp-f -r chapter0904 ../bin

         cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0904.o chapter0904.cpp

g++ chapter0904.o -g -o chapter0904

[developer @localhost src]$ make submit

cp -f -r chapter0904 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0904

count value:-1

count value:-2

该实例实现方式与++运算符重载相同,仅仅将相应的重载运算符作了替换。其中涉及到“-”运算符重载实现,程序具体解释见注释即可。总结自增型运算符重载,自增运算符由于在内置运算符中表现为前缀后缀运算区别。所以在重载实现时需要注意实现的区分,初学者掌握这点足可以应付基本的自增类型运算符重载应用。

9.6  重载下标运算符

重载实现下标运算符“[ ]”在很对应用场景中非常有用。例如在数组类型中通常用于通过下标值来访问数组中的元素。下面就下标运算符重载应用作详细讲述,同样以字符串操作类封装为实例。

9.6.1  重载下标运算符基本概念

下标运算符号的重载实现,很多情况下下标运算符可以作为快速访问数据元素的索引。C++中重载实现下标运算符规定只能作为类的成员函数重载实现,其定义的基本语法如下所示。

return_type &operator[](index);                                               //非常量下标运算符重载定义

const return_type &operator[](index) const;                          //常量下标运算符重载定义

上述两种实现重载下标运算符定义方式是根据不同的需要而定义的。通常非常量的下标运算符重载主要考虑到下标操作对应的数据表中都为非常量,并且可能需要作出具体下标对应元素的修改时使用。例如字符串对象定义为变量时,其中根据下标运算符可以修改字符串变量中单个字符值。

而常量下标运算符重载则主要针对常量类型的数据表应用的。例如此时定义字符串对象为常量字符串,则字符串中的字符只能获取不能作出修改。需要根据应用情况的不同,调用不同实现方式的下标重载运算符来实现。

9.6.2  实例9-3  封装字符串类重载下标运算符

为了演示下标运算符的重载应用情况,针对字符串操作类实例继续添加字符串对象下标重载操作功能,使得可以直接修改或者获取到字符串对象中单个字符,提供更加强大的字符串处理功能。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0905.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0905

* 源文件chapter0905.cpp

* 字符串操作类重载下标操作

*/

#include <iostream>

using namespace std;

 

class MyString                                                                                      //字符串操作类MyString定义

{

         public:

                   MyString();                                                                          //字符串操作类构造函数

                   ~MyString();                                                                       //字符串操作类析构函数

                   MyString&operator=(const char *str);                          //字符串操作类重载赋值运算符函数

                   char&operator[] (const int &index);                              //字符串操作类重载非常量下标运算符函数

                   constchar &operator[] (const int &index) const;                   //字符串操作类重载常量下标运算符函数

                   friendostream& operator<<(ostream& stream,const MyString&rightMystring);//重载输出流运算符函数

         public:

                   enum                                                                                 //枚举定义,表示字符串预分配内存大小

                   {

                            MYSTRING_LEN= 30

                   };      

         private:

                   char* m_string;                                                            //字符串操作类字符指针成员,表示字符串

                   int    m_stringLen;                                                        //字符串操作类整型变量成员,表示字符串长度

};

MyString::MyString()                                                                           //字符串操作类构造函数定义

{

         m_stringLen= MYSTRING_LEN;                                            //初始化字符串类字符串长度成员

         m_string  = new char[m_stringLen+1];                                //为字符串指针成员预分配内存空间

         memset(m_string,0,m_stringLen+1);                                             //初始化字符串指针成员指向的内存区域

}

MyString::~MyString()                                                                         //字符串操作类析构函数定义

{

         if(m_string!=NULL)

         {

                   delete[]m_string;                                                              //释放对应指向空间

                   m_string  = NULL;

                   m_stringLen= 0;                                                               //同时将字符串长度变量成员置0

         }

}

MyString& MyString::operator=(const char *str)                           //字符串操作类赋值运算符重载函数定义

{

         if(m_stringLen< strlen(str))                                                     //当前默认分配的内存空间小于传入的字符串大小

         {

                   if(m_string!=NULL)delete[] m_string;                          //则清除当前已分配的内存空间重新分配

                   m_stringLen= strlen(str);

                   m_string  = new char[m_stringLen+1];

         }

         strcpy(m_string,str);                                                                   //将传入的字符串拷贝至字符指针指向的空间

         return*this;

}

char& MyString::operator[] (const int&index)                                 //字符串操作类下标重载函数定义

{

         assert(index>=0&& index<=m_stringLen);                          //断言判断传入的下标是否在约定范围之内

         return*(m_string+index);                                                         //通过字符指针加运算,直接定位到下标指定位置

}

const char& MyString::operator[] (const int&index) const           //字符串操作类常量下标重载函数定义

{

         assert(index>=0&& index<=m_stringLen);

         return*(m_string+index);

}

ostream& operator<<(ostream&stream,const MyString& rightMystring)   //字符串操作类输出流重载函数定义

{

         stream<<rightMystring.m_string;                                                               //通过ostream对象输出字符串指针值

         returnstream;                                                                                                //返回ostream流对象

}

int main()

{

         MyStringmystring1;                                                                   //定义字符串操作类对象mystring1

         mystring1= "jack";                                                                     //通过赋值运算符重载,使用常量字符串赋值

         mystring1[0]= 'h';                                                                       //通过下标运算符访问并修改字符串第一个字符

         mystring1[1]= 'e';                                                                       //修改第二个字符

         mystring1[2]= 'l';                                                                         //修改第三个字符

         cout<<"mystring1value:"<<mystring1<<endl;                     //通过输出流运算符输出修改后的字符串对象值

         MyStringmystring2;                                                                   //定义字符串操作类对象mystring2

         mystring2= "hello";                                                                    //通过赋值运算符重载,使用常量字符串赋值

         for(inti = 0;i < 5;i++)                                                                   //根据预设定大小循环访问字符串中字符

         {

                   cout<<mystring2[i]<<"";                                                   //打印输出单个下标中的字符

         }

         cout<<endl;

         return0;

}

本实例主要通过字符串操作类中重载“[ ]”操作符演示下标访问字符串值的功能。程序主要包含主函数与字符串操作类两个部分,其中字符串操作类重载了“[ ]”、“=”以及“<<”操作符的方法定义。程序具体剖析见程序注释与后面的讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0905.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0905.o

CC=g++

 

chapter0905: $(OBJECTS)

         $(CC)$(OBJECTS) -g -o chapter0905

clean:

         rm-f chapter0905 core $(OBJECTS)

submit:

         cp-f -r chapter0905 ../bin

         cp-f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0905.o chapter0905.cpp

g++ chapter0905.o -g -o chapter0905

[developer @localhost src]$ make submit

cp -f -r chapter0905 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0905

mystring1 value:helk

h e l l o

本实例在原来字符串操作类基础之上,增加了下标运算符重载的两个方式定义的函数,同时增加了以流的方式输出字符串对象的重载操作函数。下标运算符重载函数中实际是采用指针加法运算来定位到指定位置并返回。首先需要判断下标是否在字符串有效范围之内,另外增加的输出流重载定义主要使用流ostream类对象实现输出功能。主程序中首先定义字符串操作类对象,随后应用赋值运算符将字符串常量“jack”赋值给该对象,之后通过非常量下标运算符访问字符串操作类对象并修改其中的字符并输出打印验证。

定义字符串操作类对象mystring2,初始化字符串“hello”,通过for循环控制结构运用下标运算符循环访问字符串对象中单个字符,并打印输出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: