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; }我们整体看一眼:
#includeusing 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),这时候,如果我们重载了左移运算符则无法成功读取。比如说这种情况:#includeusing 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关键字加上函数的声明。实际操作如下:#includeusing 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)下提供接口用于读取数据代码如下:
#includeusing 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声明返回值,后续发现要返回的时候再修改也不迟。
- 为了实现链式编程,返回值也是引用方式返回的