条款24:若所有函数皆需类型转换,请为此采用non-member函数
请参看如下代码:
class A{
public:
A(int xx):x(xx){
}
int getX() const;
private:
int x;
};
现在我们需要对类A进行乘法运算,现在有两种方式可以选择。
1)member函数重载operator *运算符:
class A{
...
A operator*(const A& AB) const{
this->x*=AB.x;
return *this;
}
...
};
我们可以发现,此时可以得出正确答案,那么此时我们改变一下代码呢?
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(int xx) :x(xx){
}
int getX() const;
A operator*(const A& AB){
this->x *= AB.x;
return *this;
}
void show(){
cout << x << endl;
}
private:
int x;
};
int main(){
A a1(10);
a1 = a1 * 2;
a1.show();
a1 = 2 * a1;
a1.show();
}
可以发现函数报错,其中a1=a1*2时正常,a1=2*a1是后函数报错,错误为没有与这些操作数匹配的 “*” 运算符,分析一下原因:
a1=a1*2执行正确,因为类A的构造函数我们声明为non-explicit的,即可以进行隐式转化,那么当执行该语句的时候,先执行2->A(2)的隐式类型转换,然后调用A的operator*函数,主要是调用了类A的operator *函数,代码执行正确;
a1=2*a1执行错误,为什么呢?等价于a1=2.operator*(a1),整数2并没有相应的类,也没有定义operator *(const A& a)的重载符,因此查找失败。
让我们对上面两种情况进行总结,可以看出,至于当参数位于参数列时候,同时当函数的构造函数是non-explicit时候,才会发生所谓的类型转换,如果参数并没有位于参数列或者函数的构造函数被声明为explicit的时候,则类型转换失败!
2)我们可以将其声明为non-member成员函数,见如下代码:
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(int xx) :x(xx){
}
int getX() const{
return x;
};
void show(){
cout << x << endl;
}
private:
int x;
};
A operator*(const A& a, const A& b){
A temp = a.getX()*b.getX();
return temp;
}
int main(){
A a1(10);
a1 = a1 * 2;
a1.show();
a1 = 10 * a1;
a1.show();
return 0;
}
运行结果为:
可以发现此时针对A的乘法运算满足交换律,这才是我们想要的针对A的乘法运算,注意这儿我们并没有将operator *声明为friend函数,因为getX()可以获得数据成员,为了封装性考虑,我们能避免使用friend就尽量避免使用friend,member的对立面是non-member,member的近义词是friend关键字,希望大家注意一下!
总结:当你需要为某个函数的所有参数(包括被this指针所指的那个隐喻函数)进行类型转换,那么这个函数必须是个non-member。