1、c++ primer :选择成员和非成员实现
- 赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。
- 像赋值一样,复合赋值操作符通常应定义为类的成员,与赋值不同的是,不一定非得这样做,如果定义非成员复合赋值操作符,不会出现编译错误。
- 改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常就定义为类成员。
- 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。
但是,问题是为什么?为什么赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员?
2、具体分析
2.1 调用二义性
c++默认为类生成复制操作符,如果定义赋值操作符重载函数为类的友元【全局函数】,将会引起调用二义性,举个栗子:
class A;
A& operator=(A& a1, const A& a2)
{
// TODO:
return a1;
}
class A
{
// TODO:
};
int main()
{
A a1, a2;
a2 = a1; //which operator= is called?
return 0;
}
2.2 操作语法错误
以[]操作符举例:
A& operator[](int b, A& a)
{
//...
return a;
}
int main(void)
{
A a;
3[a];
return 0;
}
同理:2=a, 6(c), 7->c 之类的代码,显然这些代码是语法错误的,原因在于当作为非成员时,函数第一个参数不在约束为this所指向的对象,所以这些定义完全有可能发生;
此外,如果A类定义了转换构造函数:
A::A(int i);
此时定义operator[]:
A& operator[](A& a, int b)
{
//...
return a;
}
int main(void)
{
3[2]; // 使用了转换构造函数 3-> class A type
return 0;
}
2.3 string类型的 operator+
另外我们可以推测string类的加号操作符重载函数是有两个版本的。
- 成员函数:string& operator+(const string& s);
- 友缘全局函数:string& opreator+(std::string s1, std::string& s2);
std::string s1 = "hello";
const char* s2 = "world";
string s3 = s1 + "world"; // call member func
string s4 = "world" + s1; // call friend func
假设二者都是调用成员函数的operator+(),那么第s4的加法语句等价于:
s4 = s2.operator+(s1);
然而s2是const char*类型,根本没有成员函数。