左移运算符重载:可以打印输出自定义数据类型。
为了输出重载,我们先看看现有的输出函数。输出类型为std下的ostream类型的引用。
标准输出流(全局只能有一个)。
返回值类型为ostream,函数名称operator<<,输入参数为我们自定义类型。
仿照加号运算符重载,我们先试试成员函数。
ostream& operator<<(Person& p) {}
利用成员函数重载 左移运算符调用情形:p.operator<<(p)。先有一个对象调用函数,然后还要输入一个对象。这不是我们期望的:cout << p;的情景。简写方式只能通过p << cout方式调用。
我们把cout也加入进去:返回一个ostream类型,这样可以形成链式编程。
ostream& operator<<(ostream& cout)
{
cout << "这个对象的m_A的值为:" << this->m_A << "这个对象的m_B的值为:" << this->m_B;
return cout;
}
这样成员函数的输入是一个ostream流的引用,输出也是一个ostream流的引用,可以满足链式需求。
调用情况:
Person p(11, 22);
// 直接调用:
//p.operator<<(cout) << endl;
// 返回类型:调用方式一。
//ostream& out = p.operator<<(cout) << endl;
// 支持这种调用。
//p << cout << endl;
输出:
这个对象的m_A的值为:11。 这个对象的m_B的值为:22
代码:
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:
Person(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
ostream& operator<<(ostream& outStream)
{
outStream << "这个对象的m_A的值为:" << this->m_A << "。 这个对象的m_B的值为:" << this->m_B;
return outStream;
}
public:
int m_A;
int m_B;
};
void test01()
{
Person p(11, 22);
// 直接调用:
//p.operator<<(cout) << endl;
// 返回类型:
//ostream& out = p.operator<<(cout) << endl;
// 支持这种调用。
p << cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
调用方式是:p<< cout。方向反了,我们希望的场景是:cout << p;
利用成员函数实现这样的场景不行了。只能采用全局函数的形式。
一般不会利用成员函数重载左移运算符<< ,因为无法实现cout在左侧的情况。只能用全局函数来实现。
简写方式是cout << p;所以应该是cout在左,在前,p在右,在后。定义一个全局函数:
void operator<<(cout, Person p) {} // 本质:operator<<(cout, p)简化形式可以写成cout << p;
本质:operator(cout, p),可简化写为:cout << p;
上面我们查到cout的类型为ostream;然后为了保证cout的全局唯一性:我们输入cout的引用。
补充函数:
void operator<<(ostream& cout, Person p) // 本质:operator<<(cout, p)简化形式可以写成cout << p;
{
cout << "m_A = " << p.m_A << "; m_B = " << p.m_B;
}
此时可调用如下:
void test02()
{
Person p(11, 22);
operator<<(cout, p);
//cout << p;
}
简化调用没问题。本质调用operator<< 也没问题。
但是cout支持链式编程,即可以在后面不断接<<,所以应该返回对于cout本体的引用。
全局函数变更为:将局部变量名换成out也可以。
ostream& operator<<(ostream& out, Person p) // 本质:operator<<(out, p)简化形式可以写成cout << p;
{
out << "m_A = " << p.m_A << "; m_B = " << p.m_B;
return out;
}
还剩最后一个问题:
在类内:一般我们把字段设置为私有private。这样在类外基于全局函数的运算符重载将无法访问到字段,所以整套的做法:要把全局函数的重载声明作为友元放在类内。
#include<iostream>
using namespace std;
#include<string>
class Person
{
friend ostream& operator<<(ostream& out, Person p);
public:
Person(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
//ostream& operator<<(ostream& outStream)
//{
// outStream << "这个对象的m_A的值为:" << this->m_A << "。 这个对象的m_B的值为:" << this->m_B;
// return outStream;
//}
private:
int m_A;
int m_B;
};
ostream& operator<<(ostream& cout, Person p) // 本质:operator<<(cout, p)简化形式可以写成cout << p;
{
cout << "m_A = " << p.m_A << "; m_B = " << p.m_B;
return cout;
}
void test02()
{
Person p(11, 22);
//operator<<(cout, p) << endl;
cout << p << endl;
}
void test01()
{
Person p(11, 22);
// 直接调用:
//p.operator<<(cout) << endl;
// 返回类型:
//ostream& out = p.operator<<(cout) << endl;
// 支持这种调用。
//p << cout << endl;
}
int main()
{
test02();
cout;
system("pause");
return 0;
}
需要总结的几点:结合上一个案例加号运算符重载:发现
- 运算符重载作为类内成员函数,在调用时是p1.operator+(p2)。可简写为p1+p2。或者p2+p1。
- 运算符重载作为类外全局函数,在调用时是opearator(p1, p2)。可简写为p1+p2。后者p2+p1。
- 也就是说必须类内成员函数输入参数有一个二元运算符中的一元,类外一元作为一元。
- 类外全局函数输入参数是二元运算符中的两个元。
- 加号无所谓方向要求。左移运算符因为有方向和顺序,所以无法用类内成员函数实现。在全局实现中也要注意参数顺序。
- 链式编程返回本体引用。