一个例子了解operator+
先来看一个题目:
#include <iostream>
using namespace std;
class A {
int a;
public:
A(int x=0): a(x){}
void f(){cout << a;}
A operator+(int op) {
A T;
T.a = a + op;
return T;
}
};
int main()
{
A a1, a2(2);
cout << a1 + a2; // A
(a1 + a2).f(); // B
(a1 + 3).f(); // C
(3 + a1).f(); // D
return 0;
}
以上 main 函数中 A,B,C,D 四个选项能通过编译?
考验对 C++ 中运算符重载的理解的时候到了!先给出答案:
cout << a1 + a2; // A --> failed
(a1 + a2).f(); // B --> failed
(a1 + 3).f(); // C --> passed
(3 + a1).f(); // D --> failed
为啥会是这样呢?看一个稍复杂些的例子(把各种情况都列一下):
#include <iostream>
using namespace std;
class A {
int a;
public:
A(int x=0): a(x){}
void f(){cout << "operator+(int): " << a << endl;}
A operator+(A op) {
A T;
T.a = a + op.a;
return T;
}
};
class B {
int b;
public:
B(int x=0): b(x) {}
void f() {cout << "operator+(Obj): " << b << endl;}
B operator+(int op) {
B T;
T.b = b + op;
return T;
}
};
int main()
{
A a1, a2(2);
B b1, b2(2);
// operator+ 的参数为 A 时,int 型 op 作为第二操作数
(a1 + 3).f(); // pass
// operator+ 的参数为 A 时,int 型 op 作为第一操作数
(3 + a1).f(); // failed
// operator+ 的参数为 A 时,A 型 op 作为操作数
(a1 + a2).f(); // pass
// operator+ 的参数为 int 时,int 型 op 作为第二操作数
(b1 + 3).f(); // pass
// operator+ 的参数为 int 时,int 型 op 作为第一操作数
(3 + b1).f(); // failed
// operator+ 的参数为 int 时,B 型 op 作为操作数
(b1 + b2).f(); // failed
return 0;
}
以上各个例子,可以发现一个规律:加法操作操作符有两个操作数,能否能够编译取决于对象是第几个操作数以及重载 operator+ 时的参数类型,且成员函数默认把 this 指针当作第一个参数传进去了。例如,(a1 + 3).f()
能 pass
是因为定义了 operator+(A)
,等价于 operator+(A, A)
,因为 3 可以通过构造函数转化为 A(3)
所以执行时相当于:第一个操作数是 a1
,所以调用的是 A
重载的加法运算符,然后用 3 实例化为一个 A 类型的对象,再传给 operator+
。所以,能否编译通过,我们要看第一个操作数的类型来确定,再根据第二个操作数来确定参数的类型,如果第一个操作数的类型中定义了该签名的 operator+
则可以编译通过,否则 failed
。所以:
// operator+ 的参数为 A 时,int 型 op 作为第二操作数
(a1 + 3).f(); // pass。目标:operator+(A, int),存在:operator(A*, A),3 可以实例化为 A,所以 passed
// operator+ 的参数为 A 时,int 型 op 作为第一操作数
(3 + a1).f(); // failed。目标:operator+(int, A),存在:operator(A*, A),不匹配,如果 A 中引入友元函数 operator(int, A) 则可以 passed
// operator+ 的参数为 A 时,A 型 op 作为操作数
(a1 + a2).f(); // pass。显然!
// operator+ 的参数为 int 时,int 型 op 作为第二操作数
(b1 + 3).f(); // pass。目标:operator+(B, int),存在:operator+(B*, int)
// operator+ 的参数为 int 时,int 型 op 作为第一操作数
(3 + b1).f(); // failed。目标:operator+(int, B),存在:operator+(B*, int),不匹配,如果 B 中引入友元函数 operator+(int, B) 则 passed
// operator+ 的参数为 int 时,B 型 op 作为操作数
(b1 + b2).f(); // failed。目标:operator+(B, B),存在:operator+(B*, int),不匹配
引入友元函数的例子:
class A {
int a;
public:
A(int x=0): a(x){}
void f(){cout << "operator+(int): " << a << endl;}
A operator+(A op) { // 其实等价于:operator+(A* , A),this 指针作为第一个参数
A T;
T.a = a + op.a;
return T;
}
friend A operator+(int op1, A op2) { // 友元函数
A T;
T.a = op1 + op2.a;
return T;
}
};
这种情况下 (3 + a1).f()
即可 passed
。
结论:根据操作数的类型和位置确定需要的 operator+ 的具体函数签名,如果在名字空间中能找到定义的函数则可以,否则 failed! 重载其他运算符的时候应该也是类似的。