子类中的重载流插入运算符 (<<)

Overloaded stream insertion operator (<<) in the sub-class

本文关键字:lt 运算符 插入 重载 子类      更新时间:2023-10-16

我有一个A类,我为它定义了一个(重载的)流插入运算符。我公开从这个 A 类派生一个 B 类,它有一个额外的数据成员。因此,我需要为派生类重新定义重载的流插入运算符,并且我是这样做的:

#include <iostream>
using namespace std;
class A {
    int i;
    char c;
public:
    A(int i = 0, char c = ' ') {
        this->i = i;
        this->c = c;
    }
    friend ostream& operator << (ostream&, const A&);
};
class B : public A {
    double d;
public:
    B(int i = 0, char c = ' ', double d = 0.0) : A(i, c), d(d) {}
    friend ostream& operator << (ostream&, const B&);
};
ostream& operator << (ostream& out, const A& a) {
    out << "nInteger: " << a.i << "nCharacter: " << a.c << endl;
    return out;
}
ostream& operator << (ostream& out, const B& b) {
    out << b;
    out << "nDouble: " << b.d << endl;
    return out;
}
int main() {
    A a(10, 'x');
    B b(20, 'y', 5.23);
    cout << b;
    return 0;
}

问题1:这是一种合适的技术吗?如果没有,请让我知道我哪里出错了。

问题 2:运行时,此程序崩溃。为什么会这样?

支持此功能的一种常见方法是让重载运算符调用虚拟成员函数:

class A { 
public:
    virtual std::ostream &write(std::ostream &os) const { 
        // write self to os
        os << "An";
        return os;
    }
};
class B : public A { 
public:
    virtual std::ostream &write(std::ostream &os) const { 
        // write self to os
        // This can use the base class writer like:
        A::write(os);
        os << "Bn";
        return os;
    }
};

然后运算符重载只是调用该成员函数:

std::ostream &operator<<(std::ostream &os, A const &a) {
    return a.write(os);
}

由于write是虚拟的,并且您传递的是 A by (const) 引用,因此这会根据实际类型调用正确的成员函数(即,A::write对象是否真的是 A 的实例,B::write如果它是 B 的实例)。

例如,我们可以像这样练习这些操作:

int main() { 
    A a;
    B b;
    std::cout << a << "n";
    std::cout << b << "n";
}

。它产生这样的输出:

A
A
B

在实际使用中,您可能希望保护write功能,并使operator<<成为A的朋友。

程序崩溃,因为第二个插入运算符调用自身:

ostream& operator << (ostream& out, const B& b) {
  out << b;  // <--YIKES!
  out << "nDouble: " << b.d << endl;
  return out;
}

一般来说,这是一个很好的方法,尽管我更喜欢给类一个公共虚拟print(ostream&) const函数,并让插入运算符调用它。