无法重载运算符<<作为会员功能

时间:2012-03-21 23:29:55

标签: c++ operator-overloading

我正在尝试将operator<<重载为成员函数。只要这样做就可以了:

我的头文件和MyClass.cc文件中的

friend ostream& operator<<(ostream& os, const MyClass& myClass);

ostream& operator<<(ostream& os, const MyClass& myClass)
{
   return myClass.print(os);
}

但是,如果我尝试关闭friend并使其成为成员函数,那么它会抱怨operator<<只能接受一个参数。为什么呢?

ostream& MyClass::operator<<(ostream& os, const MyClass& myClass)
{
   return myClass.print(os);
}

我在this question读到它不能成为会员功能,但不确定原因?

3 个答案:

答案 0 :(得分:44)

当作为成员函数重载时,a << b被解释为a.operator<<(b),因此它只需要一个显式参数(this作为隐藏参数)。

因为这要求重载是用作左操作数的类的一部分,所以它对普通ostream等没用。这需要您的重载是ostream类的一部分,而不是类的一部分。由于您不允许修改ostream,因此您无法执行此操作。这只留下全球超载作为替代方案。

然而,有一个相当广泛使用的模式,你在全局范围内重载运算符,但是调用它是一个成员函数:

class whatever { 
    // make this public, or the global overload a friend.
    std::ostream &write(std::ostream &dest) const { 
        // write self to dest
    }
};

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

当您需要多态行为时,这尤其有用。你不能让重载的运算符本身变成多态,但是你使它调用virtual的成员函数,所以无论如何它都是多态的。

编辑:以(我希望)澄清情况,你可以通过几种不同的方式做到这一点。第一个也可能是最明显的是让我们的write成员公开,并让全局运营商调用它。由于它是公开的,我们不需要做任何特别的事情让操作员使用它:

class myClass {
public:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }   
};

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
   // since `write` is public, we can call it without any problem.
   return m.write(os);
}

第二种方法是将write设为私有,并声明operator<<朋友为其提供访问权限:

class myClass {
    // Note this is private:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }

    // since `write` is private, we declare `operator<<` a friend to give it access:
    friend std::ostream &operator<<(std::ostream &, myClass const &);
};

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

第三种可能性几乎与第二种可能性相同:

class myClass {
    // Note this is private:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }

    // since `write` is private, we declare `operator<<` a friend to give it access.
    // We also implement it right here inside the class definition though:
    friend std::ostream &operator<<(std::ostream &os, myClas const &m) { 
        return m.write(os);
    }
};

第三种情况在C ++中使用了一种名为“名称注入”的相当奇怪(并且鲜为人知)的规则。编译器知道friend函数不能成为类的一部分,因此不是定义成员函数,而是将该函数的名称“注入”到周围的作用域(在这种情况下为全局作用域) 。尽管在类定义中定义了operator<<,但它根本不是 成员函数 - 它是一个全局函数。

答案 1 :(得分:10)

您可以将operator<<重载为成员函数。但你不能写一个成员operator<<,左边有一个ostream,右边是你的班级。

当你创建一个(非静态)成员函数时,有一个隐含的第一个参数,即调用对象。 operator<<是二进制的,所以它只需要2个参数。如果你把它作为一个成员函数,你只能给它一个paremeter,因为它已经有一个(调用对象)。由于该调用对象始终是第一个参数,因此您不可能将输出运算符编写为(非静态)成员(至少不是该特定函数的标准形式),因为对于这种情况,ostream需要成为第一个论点。

答案 2 :(得分:1)

可以这样想:当你想要流式传输到ostream时,你正在调用&lt;&lt;流对象上的operator 。而且你不能直接修改ostream的'私有'方法。因此,您必须创建一个重载版本,并使其成为朋友。

当您创建自己的运算符时&lt;&lt;你班上的方法,你正在创建一个&lt;&lt;将在您的类上运行的方法,而不是ostream对象。我猜你可以在你的班级内部保存一个ostream并流式传输,但你写这样的链式语句会有困难:a << b << c