C++运算符重载

来源:互联网 发布:知乎为什么垃圾 编辑:程序博客网 时间:2024/05/16 12:50

》运算符函数与运算符重载

        运算符重载是计算机语言固有多态性的体现。例如在大多数语言中,运算符“ - ”既表示两数相减,又表示取一个数的相反数;既可针对整型数据,又可针对实型、指针等其他类型的数据。C++语言进一步拓展了运算符重载的概念:它不但提供固有的重载,而且还提供重载的手段。

        C++把重载的运算符视为特殊的函数,称为运算符函数。运算符重载就是函数重载的一种特殊情况。与一般重载函数一样,编译系统能够依据使用运算符的不同环境,即参数(操作数)的数量或类型的差异,区分同一运算符的不同含义。

         “运算符重载”是针对C++中原有运算符进行的,不能通过重载创造出新的运算符。除了“  [.]  [.*]  [->*]  [::]  [?:] ”运算符外,其他的运算符都可以重载。由于很多符号是一元运算符和二元运算符公用的(如*),为了避免含混,不得为重载的运算符函数设置默认值,因此在调用时也就不得省略实参。

         除了new和delete这两个较为特殊的运算符外,任何运算符在作为成员函数重载时不得重载为静态成员函数。=、[]、()、->以及所有的类型转换运算符只能作为成员函数重载,而且不能针对枚举类型操作数进行重载。

         运算符函数的函数名由运算符前加关键字operator构成,在声明运算符或调用运算符时都可以用这个名称。因此,可以用两种不同的方法调用运算符函数。

         示 例:假定已经作为某个类的成员函数重载了二元运算符+,且c1,c2都是该类的对象。

               c1.operator + (c2)    与     c1+c2  

         含义相同。如果+作为该类的非成员函数重载,则

               operator + (c1,c2)    与 c1+c2

         含义相同。

        

         运算符函数作为非成员函数重载时,由于没有隐含的this指针,因此所有的操作数均出现在形参表中。因此,对于一元运算符,形参表中有一个参数,代表那个唯一的操作数;对于二元运算符,形参表中有两个参数,分别代表第一操作数和第二操作数。

         重载的运算符保持其原有的操作个数不变。因此,“ * ”既可重载为一元运算符,又可重载为二元运算符;但“ = ”只能重载为二元运算符。

         注:重载的运算符还保持其原有的优先级和结合性不变。


>重载运算符“ ++”

        ++既可以是前缀运算符(前增1),又可以是后缀运算符(后增1)。为了区分这两种情况,重载这两个运算符时必须在格式上有所区别:重载后缀++时必须多一个虚拟参数:int,因此从形式上看像是一个二元运算符重载。

        作为成员函数重载前缀++和后缀++:

         //前缀++

          <函数名> & operator ++ ();

         //后缀++

          <函数名> operator ++(int);

          注:上述实现方式尽可能多地保留了++原有的属性

               (1)前缀++仍然是“先增1后取值”,后缀++仍然是“先取值后增1”

               (2)对于前缀++,其操作结果仍然是通过所作用的变量提供的,因此返回的操作结果就是对该变量的引用

          作为非成员函数重载,唯一的操作数必须作为第一参数提供:

          //前缀++

           friend <函数名>& operator ++(<函数名> &<对象名>);

          //后缀++

           friend <函数名>& operator ++(<函数名> &<对象名>,int);


》运算符重载应注意的几个问题

        >重载的运算符应保持其原有的基本语义

                重载的运算符应尽可能保持其原有的基本语义,例如针对一个新的数据类型(一个新的类)重载了运算符+,则它的含义应当是“相加”、“添加”或“连接”等,而不应当是“相减”或是其他与基本语义不相干的含义。重载的运算符应该体现为原运算符功能在新的数据类型上的延伸,它的使用应当使程序中算法的表达更流畅、自然,使阅读程序的人在不借助其他说明资料的情况下就能够正确理解。如果无法达到这样的效果,宁可用一个普通函数来实现相应的功能,也不要让重载的运算符去勉强承担哪些更适合于一般函数承担的功能。

        >重载的运算符应尽可能保持其原有的特性

                运算符的操作数个数、优先级和结合性是三个最基本的特性,这些特性在重载时必须要保持。此外,重载时还要注意如下一些特性:

                (1)是否要求第一操作数为有左值操作数。左值就是数据的地址,右值就是数据本身。每个数据都有右值,但只有部分数据有左值;典型的有左值数据就是变量,无左值数据就是常量。增量减量运算符(++、--)、赋值运算符(=)、复合赋值运算符(+=、*=等)以及取地址运算符(&)都要求其第一操作数必须是有左值的操作数。重载这些运算符时应尽可能保持这一特性。在将这些运算符作为非成员函数重载(=只能作为成员函数重载)时,对应于第一操作数的第一参数必须声明为引用参数。

                (2)是否修改第一操作数。在(1)中所述的运算符中,除(&)外,都要修改第一操作数。重载这些运算符时应尽可能保持这一特性,函数体中应包含修改第一操作数的操作。

                (3)操作的结果是否为有左值数据。在(1)中所述的运算符中,除后缀增量和后缀减量运算符外,其他运算符的操作结果都是有左值数据--实际上就是改变了值的第一操作数。重载这些运算符时应尽可能保持这一特性。因此运算符函数的返回值应声明为引用;在作为成员函数重载时,第一操作数就该是对象本身,因此函数体中须用return *this;返回;作为非成员函数重载时,第一操作数就是第一参数变量,因此在函数体中须用return <第一参数变量名>;返回。

                (4)保证不改变第二操作数。所有的二元运算符都不会改变第二操作数的值,重载二元运算符时应尽可能保持这一特性。因此,应该把对应与第二操作数的参数(对于成员函数,就是参数表中唯一的参数;对于非成员函数,就是第二参数)声明为引用,并使用const加以修饰。

        >运算符的重载应当配套

                某些运算符之间关系密切,存在着某种逻辑上的联系,因此若需要重载其中的某一个,往往就意味着同组的其他运算符也需要重载。例如,重载了+(加),通常也需要重载-(减);重载了==(等于),势必也就要重载!=(不等于)。

        >使用引用参数还是非引用参数

                将具有副作用的运算符重载为非常远函数时,其第一参数代表第一操作数,应该按引用传递,以便使改变第一操作数的值称为可能。除了这种情况以外,其他参数既可以声明为非引用参数,也可以声明为引用参数。

                非引用参数的优点是以传值方式传递参数,形参变量只是实参的副本,对形参的修改不会影响实参;在相关对象存在只需一个实参的构造函数的情况下,可以充分利用表达式处理过程中的自动转换机制,使表达式显得更自然。当对象很大或需要深层复制时,非引用参数占用的计算机资源较多,影响参数传递的效率。

                引用参数的优点是当对象很大或需要深层复制时,可大大减少对资源的占用,提高参数传递的效率。但其无法利用系统的自动转换机制。程序设计者可根据上述信息并结合具体情况,选择适当的参数形式。

        >作为成员函数重载还是作为非成员函数重载

                =、[ ] 、( )  、->以及所有的类型转换运算符只能作为成员函数重载。如果允许第一操作数不是同类对象,而是其他数据类型,则只能作为非成员函数重载(如输入输出流运算符>>和<<就是这样的情况)。若希望系统在必要时能够利用只需要一个实参的构造函数自动对第一操作数进行转换,应将该运算符作为非成员函数重载;此种情况下,运算符函数的参数应该是非引用参数。其他情况下运算符一般应作为成员函数重载。

0 0
原创粉丝点击