<<运算符的重载相比其它运算符较为特殊,这里通过几个例子来搞懂运算符重载的本质以及<<运算符如何进行重载。
我们有一个Person类,他有姓名和年龄两个私有属性name和age;
p是Person的一个实例,我们想要把p打印出来,如果直接 cout << p 的话是错误的,如下:
#include<iostream>
using namespace std;
class Person
{
public:
Person(string name, int age):name(name), age(age) {}
private:
string name;
int age;
};
int main()
{
Person p("tom", 20);
cout << p;
}
正确的做法是要对输出运算符 << 进行重载。那么运算符重载有两种方式——成员函数和全局函数,这里该用哪种呢?
答案是全局函数,为什么不能用成员函数呢?
首先,运算符重载的本质就是调用重载函数,例如我们有Class A,我们对A进行了+重载,那么A的两个对象x,y, x+y等价于x.operator+(y)。
#include<iostream>
using namespace std;
class A
{
public:
int a;
A() {}
A(int a):a(a) {}
A operator+(A& p) //+运算符重载,成员函数方式
{
A sum; //这里调用A的默认构造函数
sum.a = this->a + p.a;
//这个函数要返回值而不能返回引用,因为sum是一个局部对象,在函数结束后就会被销毁。
//如果返回引用的话函数结束后引用指向的是一个无效地址,而返回值的话将会调用复制构造函数,将sum拷贝一份返回
return sum;
}
};
int main()
{
A x(100), y(200);
A z = x.operator+(y); //等价于z = x + y
cout << z.a << endl;
}
跟上面的+运算符重载一样,我们如果要用成员函数重载的话,cout << p 就应该等价于cout.operator<<§,但是cout是ostream类里面的,这就意味着我们要去修改ostream类,给它加一个<<的重载,这是很危险的行为,所以,我们选择用全局函数对<<进行重载。
事实上,我们用cout<< 打印各种基本数据类型也是重载,只不过C++的发明人帮我们写好了各种内置数据类型的重载,而对于一些自定义数据类型需要我们自己去写。
看看这两个系统已经帮我们写好的重载,加深一下理解:
double a = 1.5; cout.operator<<(a); //等价于cout << a; 这是用成员函数的方式进行重载
string b = "hello world"; operator<<(cout,s) //等价于cout << s; 这是用全局函数的方式进行重载
下面回到我们的Person类,我们已经决定了要用全局函数对输出运算符<<进行重载,重载函数大概长这个样子:
void operator<<(ostream& os, const Person& p) //第一个参数应该传cout,第二个参数传Person的对象
{
cout << "name: " << p.name << " age: " << p.age; //age和name都是p的私有成员
}
这里有一个问题,age和name都是p的私有成员,我们定义的这个函数是全局函数,不是成员函数,不能访问p的私有成员,所以要将这个函数声明为Person类的友元函数,下面看完整代码:
#include<iostream>
using namespace std;
class Person
{
public:
friend void operator<<(ostream& os, const Person& p); //声明operator<<为友元函数
Person(string name, int age):name(name), age(age) {}
private:
string name;
int age;
};
void operator<<(ostream& os, const Person& p)
{
cout << "name:" << p.name << " age:" << p.age << endl;
return os;
}
int main()
{
Person p("tom", 20);
operator<<(cout, p); //等价于cout << p;
}
//运行结果 name:tom age:20
以上代码看似没有问题,但是看我下面这句:
//这两句代码编译无法通过
Person p1("bob", 25), p2("jack", 18);
cout << p1 << p2; //相当于 operator<<(operator<<(cout, p1), p2)
这样写就会报错,为什么呢?
因为<<的优先级是从左到右,所以这段代码的执行顺序为从左到右:(cout << p1) << p2 等价于operator<<(operator<<(cout, p1), p2)
!!!但是我们operator<< 函数是没有返回值的,也就是说在operator<<(cout, p1)执行完之后,会变成这样:operator<<(NULL, p2), 显然这是错误的,因为operator<<的第一个参数必须是ostream类的对象,所以我们改一下上面的代码。
下面是完整代码:
#include<iostream>
using namespace std;
class Person
{
public:
friend ostream& operator<<(ostream& os, const Person& p); //声明operator<<为友元函数
Person(string name, int age):name(name), age(age) {}
private:
string name;
int age;
};
ostream& operator<<(ostream& os, const Person& p) //返回值为ostream类的引用
{
cout << "name:" << p.name << " age:" << p.age << endl;
return os;
}
int main()
{
Person p("tom", 20);
cout << p;
Person p1("bob", 25), p2("jack", 18);
cout << p1 << p2;
}