美文网首页
C++学习笔记(七)操作符重载(下)

C++学习笔记(七)操作符重载(下)

作者: 活体检测业余爱好 | 来源:发表于2017-08-01 14:16 被阅读17次

    1、重载下标操作符

    在前面已经提到下标操作符是必须要以类的成员函数的形式进行重载的。其在类中的声明格式如下:

    返回类型 & operator[] (参数)

    const 返回类型 & operator[] (参数)

    如果使用第一种声明方式,操作符重载函数不仅可以访问对象,同时还可以修改对象。如果使用第二种声明方式,则操作符重载函数只能访问而不能修改对象。

    在我们访问数组时,通过下标去访问数组中的元素并不具有检查边界溢出功能,我们可以重载下标操作符使之具有相应的功能。

    例1:


    本例中定义了一个Array类,表示的是一个整形数组,在类中我们重载了下标操作符,使之具备检测下标溢出的功能。在类中为了方便我们使用了string类,这个类将在后面会进行详细介绍,在这里可以将其对象理解为一个字符串。在本例中重载下标操作符,我们提供了两个版本的重载下标操作符函数:

    int & operator[]( int );
    const int & operator[]( int )const;

    需要注意的是第一个下标操作符重载函数最后面不带有const,加上const意味着该成员函数是常成员函数,如果第一个函数后面也加上了const,则两个函数仅有返回值不相同,这个不足以用于区分函数,编译器会提示语法错误。这两种版本的下标操作符重载函数其实很好理解,一个是可以修改类对象,下面一个则只可以访问对象而不能修改对象。对于上面一种下标操作符重载函数的声明,以下两个语句都是有效的:

    arr[5] = 7;
    int var = arr[3];

    换言之,我们既可以访问类对象,同时又能修改类对象。“arr[5]”其实可以理解为:

    arr.operator[]( 5 )

    而对于下面一种下标操作符重载函数,我们不能修改对象,也就是说语句“arr[5] = 7;”语句是无效的,但是它依然可以用于访问对象,因此“int var = arr[3];”语句仍然有效。

    我们再来看一下下标操作符重载函数的定义,在函数体内部,先进行下标越界检测,如果出现越界则抛出异常,否则就返回下标 i 所对应的数据。这两种版本的下标操作符重载函数的函数定义都是如此。

    我们来看一下程序输出结果:

    0
    1
    2
    3
    4
    out of bounds, i = 5

    在例1中即使我们没有定义const版本的,上面的例子也是可以正确运行的,但是非const成员函数不能处理const对象,因此通常我们在设计程序时,会同时提供两种版本的操作符重载函数。在例1中如果我们增添下面一个display顶层函数,用于打印对象数组中的所有元素。

    此时如果我们在例1中没有定义const版本的下标操作符重载函数,则例1将会出现语法错误而无法编译通过的。

    2、函数调用操作符重载

    与下标操作符重载函数相同,我们同样需要以类成员函数的形式对函数调用操作符“()”进行重载。其声明语法只有一种形式:
    返回类型 operator()( 参数列表 );

    例1:



    在这个例子中我们定义了一个Array类,这个类描述的是一个二维的数组,在类中我们先定义了一个默认构造函数,之后声明了一个带参数的构造函数“Array(int m, int n);”,所带的这两个参数分别是数组的两个维度的大小。之后声明了一个函数调用操作符重载函数“int & operator()(int, int);”和“const int & operator()(int, int)const;”,同样的,因为只有常成员函数才能处理常对象,故我们依然在类中提供两个版本的函数调用操作符重载函数。我们可以去看一下两个函数的函数定义,在它们的函数体中,我们先是做一个越界检测,当然对于二维数组而言,边界是有两个的,因此有两次边界检测的。如果没有越界则会返回对应的值。有了这两个函数调用操作符重载函数,我们就可以用A(i,j)的形式访问二维数组中的数据了。

    当我们用A(i,j)的形式访问二维数组中的数据时,A(i,j)会调用类中的函数调用操作符重载函数,此时A(i,j)可以理解为:

    A.operator()(i, j)
    例1中程序运行结果如下:
    0 1 2 3 4 5 6 7 8 9 10 11
    1 out of bounds!
    2 out of bounds!

    在例1中的主函数中异常捕获语句,我们先运行的是A(5, 3),故而是第一个边界越界了,因此先抛出“1 out of bounds!”的异常,而后又运行A(2, 6),此时为第二个边界越界,抛出“2 out of bounds!”的异常。

    3、重载自增与自减操作符

    自增“++”与自减“--”都是一元操作符,其前置和后置两种形式都可以被重载。有了前面介绍操作符重载的基础,我们就直接以示例的形式介绍自增与自减操作符的前置与后置重载方法。

    例1:


    在本例中我们定义了一个简单的秒表类,该类有两个私有成员变量min和sec,分别代表分钟和秒钟。在类中声明的成员函数setzero是用于秒表清零,run函数是用于描述秒针向前进一秒的动作,之后是三个操作符重载函数,前两个分别是重载自增操作符,最后一个是重载输出操作符。我们来仔细看一下各个函数的具体实现。先来看一下run函数的实现,run函数一开始让秒针自增,如果此时自增结果等于60了,则应该进位,分钟加1,秒针置零。再来看一下operator++()函数的实现,该函数时实现自增的前置形式,因此直接返回run()函数运行结果即可。对于operator++ ( int n )函数,这是实现自增的后置形式,自增的后置形式返回值是对象本身,但是之后再次使用该对象时,该对象自增了,因此在该函数的函数体中,先将对象保存,然后调用一次run函数,之后再将先前保存的对象返回,在这个函数中参数n是没有任何意义的,它的存在只是为了区分是前置还是后置形式。最后我们还重载了输出操作符,以便于我们打印计时结果,在函数体中我们使用了输出控制函数,这些函数将在输入输出流那一章会有介绍。

    这个程序运行结果如下:

    s1 00:00
    s2 00:01
    s1 00:01
    s2 00:01

    对照主函数来看程序运行结果,主函数一开始我们定义了两个对象s1和s2,第一次操作是s1 = s2 ++; 采用的是后置形式,这可以理解为s1 = s2 并且s2自增,输出结果是s1是处于置零状态,s2自增了一秒钟。之后两个对象都清零,清零后s1 = ++ s2; 这个可以理解为s2自增并将自增结果赋给s1,如此一来两个对象都自增一秒钟。

    自减操作符的重载跟自增操作符类似,这里就不再赘述了。

    4、重载转型操作符

    在前面我们已经介绍过转型构造函数,转型构造函数可以将其它类型的参数转换为类类型,如果我们要进行相反的转换过程,将类类型转换为其它数据类型,则需重载转型操作符。转型操作符重载函数的声明语法如下:

    operator 类型名 ();

    转型操作符重载函数有几点需要注意的:
    1)函数没有返回类型;
    2)虽然没有返回类型但是函数体中必须有return语句,其返回类型是由类型名来指定的;
    3)转型操作符重载函数只能以类的成员函数的形式进行重载,而不能以友元函数或顶层函数的形式进行重载。

    例1:


    在本例中我们重载了一个时钟类clock,该类中我们声明了一个转型操作符重载函数,该函数可以将类类型的时间转换为一个整形,转换后的整数是军事时间。在主函数中我们定义了一个clock类的对象c,之后将其赋给一个整形变量time,因为我们定义了转型操作符重载函数,因此这一句话并没有出现语法错误。

    转型操作符重载可以给程序带来一定的方便,但是建议还是谨慎使用。因为系统通常在需要的时候就会调用转型操作符重载函数,该函数的调用时隐式的,有时候会给程序带来一些意想不到的问题。

    5、内存管理操作符重载

    内存管理操作符new、new[]、delete和delete[]同样也可以进行操作符重载,其重载形式既可以是类成员函数的形式,又可以是顶层函数的形式。在设计一般的程序时,使用内建的内存管理操作符基本够用了,只有在需要进行自己的内存管理时才会对内存管理操作符进行重载。

    操作符new的重载函数有两种形式:

    void * 类名::operator new ( size_t size )|
    {
        //以类成员函数的形式重载new操作符
    }

    void * operator new ( size_t size )
    {
       //以顶层函数的形式重载new操作符
    }

    两种方式的重载函数返回值都是相同的,返回值都是void *类型。重载函数的参数都有一个参数,且均为size_t类型。在重载new和new[]操作符时,无论是以何种形式进行重载的,重载函数的第一个参数必须是size_t类型,该参数表示的含义是要分配空间的大小,对于new[]的重载函数而言,size_t类型参数则表示所需要分配的所有空间的总和。当然,重载函数也可以有其它参数,其它参数可以根据需要增加,但是第一个参数必须是size_t类型。如下面的重载是错误的:

    void * 类名:: operator new( void * ptr) {  }

    同样的delete操作符也有两种重载形式:

    void 类名:: operator delete ( void *ptr)
    {
       //以类成员函数的形式重载delete操作符
    }

    void 类名:: operator delete ( void *ptr)
    {
       //以类成员函数的形式重载delete操作符
    }

    两种重载方式均是返回void,并且两种重载方式都必须有一个参数,void 指针,该指针指向需要释放的内存空间。重载delete和delete[]操作符同样也都必须有一个参数,该参数为一个void型指针,指向需要释放的内存空间,当然重载函数同样可以根据需要添加其它参数,但是第一个指向待释放的内存空间的void型指针这个参数必不可少。

    当我们以类成员函数的形式重载了new和delete操作符时,其使用方法如下:

    当然如果类中没有定义new或者delete的重载函数,则系统会自动调用内建的new或delete操作符。

    相关文章

      网友评论

          本文标题:C++学习笔记(七)操作符重载(下)

          本文链接:https://www.haomeiwen.com/subject/pxjjlxtx.html