问题简介

在 C++ 中,为自定义类型实现流插入/提取(operator<< / operator>>)是常见需求。
但许多初学者会疑惑:为什么这些运算符不能直接作为我的类的成员函数来实现,以便直接写 std::cout << obj;

本文不讲较多概念,只为用最简单的语句快速让你理解为什么不能重载为成员函数,因此部分语句比较口语化或不够严谨,请多多见谅啦~


为什么不能重载为成员函数?

成员函数参数的“真面目”

在C++类的成员函数中,如果我们想要访问调用当前方法的对象的内部变量,我们是可以直接使用的,就像这样:

class ClassA
{
public:
	void Test(int x)
	{
		_a = x;
	}
private:
	int _a;
};

但是,如果是学过Java的同学应该都知道,在Java中,我们需要使用this关键字来对当前调用对象进行访问。
没错,在C++中,我们一样可以使用this关键字,来代表当前调用函数的对象

class ClassA
{
public:
	int getA()
	{
		return this->_a;
	}
private:
	int _a;
};

可能大家看到这里有点疑惑,为什么要介绍this关键字呢?
因为在C++的成员函数中,存在一个“隐藏的参数——this”!
就以上面的getA函数为例,我们没有写任何参数,但是其实它的声明的“真实面目”是这样的:

int getA(A* this)

换句话说,每一个成员函数的参数列表都有一个隐藏的this指针,并且参数中的this指针一定在第一位,作为第一个参数
看到这里,我们就可以开始解释为什么作为成员函数的流运算符无法正常使用了


流运算符作为成员函数为啥无法正常使用

在这里首先要补充一个点:
在C++的双目运算符中,在符号左边的作为第一个参数传入,在符号右边的作为第二个参数传入

此时,我们再来看看错误的在成员函数里的写法

ostream& operator<<(ostream& out);

按照这种写法,根据上文的内容我们知道,它的“真面目”应该是

ostream& operator<<(类名* this, ostream& out);

但是问题来了,我们如果想像std::string那样使用,我们就会写

my::string s("hello world");
cout << s << endl;

这样我们就能看出问题了,因为ostream作为第一个参数传给了类名* this,而字符串却传给了第二个参数ostream& out,因此就引发了报错。


正确写法:重载到类的命名空间中

举个例子,我们想要实现一个简单的my::string类,那么大体代码框架如下:

namespace my
{
	class string
	{
	public:
		string(const char* str = "");
		string(const string& s);
		~string();
		//此处省略大量成员函数
	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
	};
	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
}

可以看到,这里将operator<<operator>>都重载到了namespace my之中,并且参数列表也完全符合cout << 对象的格式,这样我们就可以成功的像std::string一样使用它!


(文章如有错漏,欢迎在评论区/私信中指出!)
如果希望看到更详细、更严谨的讲解,可以看看我的另一篇文章:
CSDN文章:C++流运算符重载为何不能作为类的成员函数?

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐