9.5.4 友元函数形式和成员函数形式的比较
对于绝大多数可重载操作符来说,两种重载形式都是允许的,但对下述运算符,只能使用成员函数形式,如表9.2所示。
关于两种重载方式的不同,还有一点要特别强调,涉及类型转换的问题。第2章中已经就C++内置类型的相互转换和转换发生的场合进行了介绍,本章稍后会详细讨论用户定义类的类型转换。
代码9.7 和代码9.8分别以成员函数形式和友元函数形式重载了complex类对象的四则运算符,仅举加法操作为例,如下所示。
complex operator+(const complex&);//成员函数形式 friend complex operator+(const complex&,const complex&);//友元函数形式对两种形式来说,下述代码都是合法的。 complex c1(1.0,2.0),c2(3.0,4.0),cRes; cRes=c1+c2;代码9.7和代码9.8中,complex类的构造函数如下所示。 complex(double r=0.0,double i=0.0) { real=r; imag=i;
}
据此可知两种重载的加法操作符都可这样使用。
complex c1(1.0,2.0),cRes; cRes=c1+5;这里,编译器会根据加法操作符的参数要求对5进行隐式转换,转换的依据就是complex类是否有合适的构造函数,代码9.7和代码9.8中complex类都提供了一个所有参数都有默认值的构造函数,因此,上述代码实际上相当于如下所示。 complex c1(1.0,2.0),cRes; cRes=c1+complex(5,0);可在下述代码的处理上,因为重载方式的不同,编译器会进行不同的处理。 complex c1(1.0,2.0),cRes; cRes=5+c1;
仅仅是改变了两个操作数5和c1的位置,意义却发生了较大的变化,如果加操作符是以友元函数方式定义的,该代码没有问题,可如果是以成员函数方式定义的,编译器就会报错,问题仍然出在对5的类型转换上。
在友元函数方式下,编译器将上述代码解释为如下所示。
cRes=operator+(5,c1);
这时,编译器会尝试根据合适的构造函数将5转换为complex类临时对象,完成参数匹配,实现加法操作。
在成员函数方式下,编译器将上述代码解释为如下所示。
cRes=5.operator+(c1)
此时,编译器不会试图将5转换成complex类的临时对象,因此,5不能调用complex类的成员函数,编译器报错。
可见,将操作符定义为友元函数形式可让程序更容易实现类型的自动转换,使两个操作符都被当成函数的参数。
注意
类型的自动转换仅发生在第2章中介绍的几种场合下。
如果程序中大量用到了complex类对象和int、double等的操作,最好的方式是重载几个complex类和内置数据类型(如int、doule等)相加的版本,编译器根据操作数选择具体调用哪个版本,这和函数重载本质上是相同的,关于编译器如何选择合适的版本调用的机制将在本章稍后的内容中进行详细介绍。
注意
重载、类型自动转换以及从多个函数中选择最优,这些问题交织在一起,在看完本章稍后的介绍后做总体考虑,会有新的收获。