为什么以及如何重载 operator<< 用于打印

Why and how to overload operator<< for printing

我写了一个实现栈的程序。而且我里面有一个显示功能

一开始我是这样写显示函数的:

template <class t>
void Mystack<t>::display()
{
    for (int i = 0; i <= top; i++)
    {
        std::cout << input[i] << " ";
    }
}

然后开发人员建议我写一个显示功能更通用。所以我把显示函数写成:

template <class T>
void Mystack<T>::display(std::ostream &os) const         
{
    for (int i = 0; i <= top; i++)
    {
        os << input[i] << " ";
    }
}

根据我的理解,编写上述函数的好处是现在我有一个通用的显示函数,我也可以使用它来显示数据到控制台或文件。

问题一:我的理解对吗?

现在另一个建议是编写如下函数:

template <typename T>
friend std::ostream& operator<<(std::ostream& s, Mystack<T> const& d) {
    d.display(s);
    return s;
}

问题2:拥有以上显示功能有什么好处?拥有以上展示功能,究竟能达到什么目的呢?

对于问题1,你的理解是正确的,但真正的提升来自于问题2的写作建议:

template <typename T>
friend std::ostream& operator<<(std::ostream&, Mystack<T> const& );

这将使任何人都可以像流式传输其他任何内容一样流式传输您类型的对象:

std::cout << "Hi, my stack is " << stack << ", it has size " << stack.size();

他们想要的任何流:

some_file << "Result of computation is: " << stack;
std::cerr << "Error, invalid stack: " << stack << ", expected: " << some_other_thing;

首先 - 。通过使用 std::ostream& 参数,您也可以输出到任何派生流,例如 std::ofstreamstd::coutstd::cerr.

使用 operator<< 允许您使用该运算符。考虑:

mystack<int> stackOfInts;
//...
std::cout << "Stack contents:" << std::endl << stackOfInts << std::endl;

它只是比 'standard' 函数调用更加地道。

返回流允许运算符链接,如上例所示。链接有效地将调用 operator<< 的结果传递给另一个:

operator<<( operator<<("Stack contents:", std::endl), stackOfInts ) );

如果这个重载调用也没有 return 一个 std::ostream&,那么就没有办法了:

operator<<( resultOfAbove, std::endl );

声明函数 a friend 允许其定义使用私有成员。如果没有这个,你将不得不为每个私人成员写一个 public getter。

关于问题 2,如果您有基础 class 和大量派生的 class,这就是解决方法,并且只想在基数 class 中写一次 operator<<。然后,如果您沿 class 层次结构将 display() 函数声明为 virtual,则可以在运行时选择正确的显示函数。也就是说,如果你有类似

Derived foo;
std::cout << foo;

然后 Base::operator<< 将调用 Derived::display(),因为它被标记为虚拟并且 foo 通过引用传递。当您有一个 class 层次结构并且不想为每个派生的 class 重载 operator<< 时,这是一种方法。

这是避免代码重复的常用技巧。见

Making operator<< virtual?

在 Whosebug 上获取更多详细信息。

两者的显示功能基本相同。不同的是你调用函数的方式。 对于第一个函数,您可以按常规方式调用函数:

std::cout<<"This is MyStack (with first method): ";
m.display(std::cout);      //function call
std::cout<<std::endl;

对于第二个函数,您可以使用运算符“<<”调用函数:

std::cout<<"This is MyStack (with second method): "
               <<m   //function call
               <<std::endl;

不过我个人更喜欢第二种。因为比较熟悉