关于重载运算符<<的问题

3
尝试学习C ++函数模板。以下是我所学的一部分代码。它可以正常工作,但我有以下问题:
1]为什么运算符<<重载函数需要是友元?如果我删除关键字friend,它会给出编译错误,说operator<<具有太多参数。
2]为什么运算符<<重载函数需要返回对ostream对象的引用,该对象也是其输入参数?
3]我怀疑这个问题,但以上两个问题是否与使用函数模板用于具有重载函数的用户定义类有任何关系?
template <class T>
T Average(T *atArray, int nNumValues)
{
    T tSum = 0;
    for (int nCount=0; nCount < nNumValues; nCount++)
        tSum += atArray[nCount];

    tSum /= nNumValues;
    return tSum;
}

class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents)
        : m_nCents(nCents)
    {
    }

    //Why is friend needed below

    //Why does it need to return ostream&, why can't it have void return type, as all it is doing is printing the class private member.
    friend ostream& operator<< (ostream &out, const Cents &cCents)
    {
        out << cCents.m_nCents << " cents ";
        return out;
    }

    /* 
    void operator <<( const Cents &cCents) //did not work - compilation errors
    {
        cout << cCents.m_nCents << " cents ";
    }
    */

    void operator+=(Cents cCents)
    {
        m_nCents += cCents.m_nCents;
    }

    void operator/=(int nValue)
    {
        m_nCents /= nValue;
    }
};

int main()
{
    int anArray[] = { 5, 3, 2, 1, 4 };
    cout << Average(anArray, 5) << endl;

    double dnArray[] = { 3.12, 3.45, 9.23, 6.34 };
    cout << Average(dnArray, 4) << endl;

    Cents cArray[] = { Cents(5), Cents(10), Cents(15), Cents(14) };
    cout << Average(cArray, 4) << endl;

    cin.get();
    return 0;
}
3个回答

3
为什么需要将运算符<<重载函数声明为友元?如果我去掉关键字“友元”,编译器就会报错说:操作符<<有太多的参数。 <<改变了流的状态,因此理想情况下应该将其实现为其左操作数类型的成员。然而,它的左操作数是标准库中的流,虽然大多数由标准库定义的流输出和输入运算符确实被定义为流类的成员,但在为自己的类型实现输出和输入操作时,无法更改标准库的流类型。 这就是为什么需要为自己的类型实现这些(<<和>>)运算符作为非成员函数。由于您需要在运算符定义内部访问类对象的私有/受保护成员变量,因此需要将这些重载运算符声明为类的友元。
为什么需要将运算符<<重载函数返回对其也是输入参数的ostream对象的引用?
返回标准流对象的引用允许您进行对象链接。
您可以进行以下调用:
out<<obj1<<obj2;

模板被用于用户定义的类,该类具有重载函数?

模板帮助您实现通用函数和类,可以为不同的数据类型调用,并且编译器负责为这些特定的数据类型生成代码。因此上述两点并不相关。


强烈建议阅读此FAQ条目:
运算符重载


谢谢。我有一个疑问,为什么运算符>>不能是类成员函数?因为最终我要用operator <<与用户定义类的对象一起使用,不是吗?为什么它需要是非成员/通用函数? - goldenmean
@goldenmean:我的回答的第一部分涉及到了这个问题,你应该阅读我在答案中添加的链接,这将帮助你更好地理解并解决你所拥有的疑问。 - Alok Save

1

[1] 它需要成为友元,因为它试图访问私有成员变量m_nCents,该变量只能被Cents的成员函数或Cents的友元访问。

[2] 这是重载“流操作符”的标准函数签名。这使得可以连接<<

out << a << b;

相当于

(out << a) << b;

编辑: 看起来你想让operator<<成为类的成员。这是不可能的,因为它的第一个操作数是ostream而不是Cents。实际上,friend声明它为非成员友元函数。如果你省略friend关键字,在类定义内部声明它作为成员函数(因为它有两个参数在签名中,还有隐式的Cents作为第一个参数,即被调用的对象,如果你喜欢的话,还有this指针)。

将流输出运算符operator<<声明为非成员函数是标准的做法,无论是否使用friend(取决于你的情况,你需要friend,或者以某种方式公开m_nCents)。


0

它必须返回一个 ostream 并接受一个参数,以便您可以链接多个调用。

例如:

Cents a,b,c;
cout << a << b << c;

它必须是一个友元,因为它不是成员函数,无论你是否在class代码块内定义。

让我进一步解释这个概念。上面的例子等同于以下内容:

op(op(op(cout,a),b),c);

'op' 是重载运算符的实际函数名称的速记。请注意,它不是在 Cents 的实例上被调用的,事实上也没有 this 指针,因为它就像一个独立的函数存在于类外部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接