第十一章 使用类(3)重载运算符

(三)重载运算符:

1.作为友元非成员函数还是成员函数

    两个操作数的运算符,如果运算符重载成员函数版本,那么第一个操作数通过this指针隐式地传递,另一个操作数通过函数参数显示传递。而如果重载是友元版本,两个操作数都是作为参数来传递的非成员版本的重载运算符函数所需的参数与运算符的操作数的数目相同,而成员函数版本的重载运算符函数所需的形参数目比运算符的操作数数目少一个,这是因为第一个参数是隐式传递的this指针,也就是调用函数的对象的指针

    有时候这两种方式会造成二义性,比如+运算符。A+B表示A.operator+(B);这是成员函数,而另一种友元函数表示operator+(A,B)。这样,A+B便会有两种函数可以实现,这就是二义性。实际编程中,我们只需要选择一种格式来编程就可以了,否则容易出现二义性错误,那么我们要怎样选择呢?对于有些运算符(比如(),[],=等),只能够使用成员函数唯一合法),而其他情况下,这两种格式差别不大,选择哪一种都可以(但是只能选一种)。有时候友元函数可能更好一点。

2.作用域,名称空间和权限

    要谈的第一点是关于作用域和名称空间。作用域指的是这个变量或函数或类等能够有访问权限,或说能起作用的区域。而名称空间是它的名字能够使用的前提。二者有一定的重合度,但是又不相同,有时候我对某个变量有访问的权利,但是又不能直接使用这个名字,因为它可能处在一个名称空间中。此时我们要访问这个变量,就要加上作用域解析运算符::。比如友元函数使用类的变量,比如Time类的变量x,此时这个变量处在友元函数的作用权限中,然而有访问权限但是不能看到,也就是说友元函数能够使用这个变量,但是这个变量名称友元函数不能直接使用,所以就需要用域解析运算符::,比如Time::x。

    再比如,如果类和它的友元函数都处在一个名称空间中,那么当我在main函数中使用类中的公共的静态类常量时候,虽然我有使用的权限,但是必须要加上名称空间和类名的作用域解析运算符(比如VECTOR::Vector::POL,前面一个是名称空间,中间是类名,最后是类中的枚举值)标准的作用域解释是使用权限+可见性,但有时候我们也可以将使用权限理解为作用域,而可见性是能够使用名称的能力,需要使用各种作用域解析运算符。

    也可以这么理解,函数有时候可以使用某个名称,这就是有权限使用,但是这个名称不一定对这个函数可见,此时,我们需要使用作用域解析运算符来进行限定,让这个名称是可见而唯一的。作用域就是指名称起作用的区域,比如类的成员函数的声明和定义,当定义的时候,函数头是不在类作用域之内的,所以要使用作用域解析运算符,但是函数体是处于类作用域内的,所以函数体内可以直接使用类中的成员。

3.状态成员

    可以使用静态类常量或者是枚举值作为状态成员(一般用枚举,含义清晰),从而标记对类对象采用的不同处理方式。一般在类外就可以使用的一些类的公共符号常量,都是通过类的枚举定义的。

    如果方法通过计算得到了一个新的类对象,则可以考虑是否通过类构造函数来完成这个工作,这样不仅可以避免麻烦,而且可以确保新的对象是按照构造函数的规则来生成的。

4.随机数

     rand()会使用一个初始种子来生成随机数,然后用生成的随机数作为种子再生成其他的随机数,因此,如果初始种子相同,则生成的随机数列是相同的(这是一种伪随机)。一般我们可以使用time(0)来返回系统时间给srand()函数来覆盖rand()函数的初始值。比如srand(time(0))。cstdlib包含了rand和srand,而ctime包含了time()。time()函数返回一个值,即格林尼治时间1970年1月1日00:00:00到当前时刻的时长,时长单位是秒。time()函数有两种用法(虽然只有一种原型,但是因为返回值和参数都可以传递结果),用法1是使用其返回值:time_t tim;tim=time(0);用法2是使用参数直接将结果放在参数指向的地址中(参数是一个指向time_t类型数据的地址),比如time_t * tim2;time(tim2);。

5.一元运算符重载

    对于一元运算符,因为只有一个操作数,因此对于类来说,这个一元运算符一定是操作这个类的对象的,所以一元运算符的规则与二元运算符有些许不同,即它不再区分左侧还是右侧,只要有类对象,就表示这个重载的运算符是这个类对象调用的。因此一元运算符重载的时候没有参数,只是在其内部实现操作就可以了。一元运算符也可以通过友元函数来重载,此时参数是相应的类对象,当使用的时候,编译器会根据一元运算符的参数来确定调用哪个函数。

你可能感兴趣的:(第十一章 使用类(3)重载运算符)