为什么必须引用流插入重载中的正确操作数?

Why the right operand in stream insertion overloading have to be reference?

本文关键字:操作数 重载 引用 插入 为什么      更新时间:2023-10-16

对于C++插入重载,我们通常执行以下操作:

ostream& operator<<(ostream& os, X& object){
// operation
return os;
}

我的问题是:为什么它必须引用 X 类型的变量对象?

我的问题是:为什么它必须引用变量 X型对象?

您不需要参考,这不是强制性的。无论如何,考虑到,由于您不打算修改对象,因此最好将其编写为:

ostream& operator<<(ostream& os, const X& object){
// operation
return os;
}

简单的替代方法是按值传递对象,方法是写入:

ostream& operator<<(ostream& os, X object){
// operation
return os;
}

问题出在哪里?您的程序将继续工作,但其性能将受到影响。将指针传递给对象(这就是引用的最终含义)不如复制整个对象有效。假设X有以下实现,即它实际上是GpsTrack类:

/** A complete track, composed by GpsPoints. */
class GpsTrack {
public:
/** Creates a new, empty track. */
GpsTrack()
{}
/** Adds a new point to the track.
*  @param p The new point.
*/
void add(const GpsPoint &p)
{ gpsPoints.push_back( p ); }       
/** @return The point at position i. */
const GpsPoint &operator[](int i)
{ return gpsPoints[ i ]; }
/** @return The number of points in the track. */
int size() const
{ return gpsPoints.size(); }
/** @return A textual representation of the track. */
std::string toString() const;
/** Adds support for the << operator. */
friend std::ostream& operator<<(std::ostream& os, const GpsTrack& track)
{ os << track.toString(); return os; }
private:
std::vector<GpsPoint> gpsPoints;
};

为了这个例子,让我们以一台 32 位机器为例。每个GpsPoint包含一对double,每个 存储 8 个字节,因此总共 16 个字节。如果GpsTrack包含 5000 个点,则结果约为 78KB。这由std::vector透明地存储在堆中。

假设我们没有上面给出的实现,而是以下实现operator<<().

/** Adds support for the << operator. */
friend std::ostream& operator<<(std::ostream& os, GpsTrack track)
{ os << track.toString(); return os; }

然后,为了使该轨道operator<<()内工作,C++将复制该对象。std::vectorgpsPoint将被透明地复制到另一个向量中,在堆中复制其信息(在方法的开头分配,在方法结束时解除分配)。但是,此信息由GpsPoint组成,在最坏的情况下(取决于其实现),这意味着运行所有点并调用其复制构造函数。这不仅是浪费时间,也是浪费资源(即内存)。

您可以将其作为 const 引用传递,它与指针一样具有性能,并且与按值传递一样安全(不允许修改)。

希望这有帮助。

无需使用const引用,也可以按值传递。两个版本同样有效

ostream& operator<<(ostream& os, const X &object);
ostream& operator<<(ostream& os, X object);

const 引用的原因通常与其他函数相同:对于(大)对象,复制(即按值传递)比仅传递引用更昂贵。

另一个原因:即使在没有可用的复制构造函数的情况下,const&也可以工作。

好吧,通常我们要么通过值传递,要么通过常量引用传递。以下是Microsoft VS2017实现的示例。它通过 const 引用传递字符串,因此我们避免复制并且仍然保证不更改对象。

template<class _Elem,
class _Traits,
class _Alloc> inline
basic_ostream<_Elem, _Traits>& operator<<(
basic_ostream<_Elem, _Traits>& _Ostr,
const basic_string<_Elem, _Traits, _Alloc>& _Str)
{   // insert a string
return (_Insert_string(_Ostr, _Str.data(), _Str.size()));
}