05-2-1 运算符重载-输入输出-左移运算符
左移运算符重载的作用:让结构体或者类可以直接完成左移运算符,无需将数据单独从结构体或者类拿出来。常见的需要用到左移运算符(<<
)的场景是:使用cout
输出。
完成左移运算符重载有、且只有一种方法:
- 在全局函数里面实现重载。
使用重载的时候,需要使用关键字operator
。左移运算符重载的函数声明是(准确说是以cout
为例):ostream& operator<<(ostream& cout, 结构体/类名称 变量);
为什么我们需要左移运算符重载:
-
因为我们在使用类的时候,类如下:
class test { public: int m_A; int m_B; };
这时候实例化出一个对象,
t1
。我想要打印t1
里面的成员属性的数据,需要访问t1
才可以获取。比如说:cout << t1.m_A << t1.m_2 << endl;
如果直接打印
t1
比如下面这样是会报错的。cout << t1 << endl;
但是,我们可以使用左移运算符的重载来实现直接输出自定义的数据。
-
为什么是重载左移运算符(
<<
)而不是其他的比如cout
。-
因为如果我们在不进行重载左移运算符的使用输入如下的代码:
cout << t1 << endl;
会报错,但是报错的不是
cout
,而是t1
前面的<<
:没有与这些操作数匹配的 "<<" 运算符
所以我们需要重载的是左移运算符(
<<
)而不是其他的东西。 -
cout
不是运算符。我们在写 c++代码的时候,需要包含头文件:
#include <iostream>
。如果涉及到输入输出的,需要使用using namespace std;
。这里面定义了cout
的作用。cout
是标准输出流下面的功能,而不是一个运算符。
-
如何进行左移运算符重载:
-
在全局函数里面实现重载。
不同于四则运算的运算符重载,左移运算符重载只可以使用全局函数。
如果在成员函数里面重载,本质上是一个类的对象调用一个函数,这时候还需要传入一个参数。比如:
p.operator<<(cout)
,简化版本是:t1 << cout
,不能实现cout << t1
这样的重载,但是可以实现t1 << cout
这样的重载。所以不用成员函数重载左移运算符的重载是因为不可以实现cout
在t1
的左侧。cout
是一个ostream
(标准输出流)的函数,全局只可以有一个,所以传入的时候需要引用的方式传入。返回值也是一个
cout
,也还是全局只可以有一个,所以也需要引用的方式返回。事实上,在this
指针([03-2 this指针](./03-2 this指针.md))中我们讲过一个链式编程思想: 比如cout
。cout
使用左移运算符(<<
)可以无限链接。比如:cout << "你好" << "我不好" << "啊?" << "我不好" << "听不见" << "n**l" << endl;
无限链接下去。所以我们返回的时候,也必须要引用的方式返回,才可以让这个链一直链接下去。
ostream& operator<<(ostream& cout, class test& p) { cout << "m_A = " << p.m_A << " m_B = " << p.m_B; return cout; }
我们整体看一眼:
#include
using namespace std; ostream& operator<<(ostream& cout, class test& p); class test { public: int m_A; int m_B; test() { m_A = 10; m_B = 20; } }; int main() { class test t1; cout << t1 << endl; return 0; } ostream& operator<<(ostream& cout, class test& p) { cout << "m_A = " << p.m_A << " m_B = " << p.m_B; return cout; } 运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)
m_A = 10 m_B = 20
-
如何访问私有数据
但我们使用类的时候,很多时候是采用私有权限(
private
),这时候,如果我们重载了左移运算符则无法成功读取。比如说这种情况:#include
using namespace std; ostream& operator<<(ostream& cout, class test& p); class test { private: int m_A; int m_B; public: test() { m_A = 10; m_B = 20; } }; int main() { class test t1; cout << t1 << endl; return 0; } ostream& operator<<(ostream& cout, class test& p) { //cout << "m_A = " << p.m_A << " m_B = " << p.m_B; return cout; } 这时候,函数
operator<<
或者说左移运算符(<<
)重载中会报错。这是因为,对象中的m_A
和m_B
成员属性是一个私有的(private
)。如果我们希望能够成功打印数据,有两种方法:
-
使用友元(
friend
)被友元标记的函数可以直接访问私有的数据(
private
),其语法是在类中开头使用friend
关键字加上函数的声明。实际操作如下:#include
using namespace std; ostream& operator<<(ostream& cout, class test& p); class test { friend ostream& operator<<(ostream& cout, class test& p); private: int m_A; int m_B; public: test() { m_A = 10; m_B = 20; } }; int main() { class test t1; cout << t1 << endl; return 0; } ostream& operator<<(ostream& cout, class test& p) { cout << "m_A = " << p.m_A << " m_B = " << p.m_B; return cout; } 比如上面的例子中
friend ostream& operator<<(ostream& cout, class test& p);
,就是设置函数operator<<
为类test
的友元。 -
在公共权限(
public
)下提供接口用于读取数据代码如下:
#include
using namespace std; ostream& operator<<(ostream& cout, class test& p); class test { private: int m_A; int m_B; public: test() { m_A = 10; m_B = 20; } int Getm_A() { return m_A; } int Getm_B() { return m_B; } }; int main() { class test t1; cout << t1 << endl; return 0; } ostream& operator<<(ostream& cout, class test& p) { cout << "m_A = " << p.Getm_A() << " m_B = " << p.Getm_B(); return cout; } 当然,在使用公共接口获取数据的时候需要注意,重载函数中需要以函数的方式获取属性的数据值。
-
- 注意:
- 完成左移运算符重载只有一种方法:在全局函数里面实现重载。其函数声明是(以
cout
为例):ostream& operator<<(ostream& cout, 结构体/类名称 变量);
。 cout
是一个标准输出流(ostream
)的函数,有,且只有一个,所以必须使用引用的方式传入函数。
- 完成左移运算符重载只有一种方法:在全局函数里面实现重载。其函数声明是(以
- 提示:
- 为了实现链式编程,返回值也是引用方式返回的
cout
。 - 如果类里面的数据是私有(
private
),可以使用友元(friend
)将左移运算符重载的函数设置为友元,这样可以直接访问。当然,也可以提供公共权限(public
)下的接口。 - 在开发中,如果在写函数的时候,不知道要返回什么的话可以先使用
void
声明返回值,后续发现要返回的时候再修改也不迟。
- 为了实现链式编程,返回值也是引用方式返回的
:-)