Type: concept
Confidence: 0.90
Created: 2026-04-16
Updated: 2026-04-16
Tags: C++运算符重载OOP最佳实践C++编程

C++流运算符重载实践

概述

operator<< / operator>> 的标准实现模式:非成员函数 + friend 声明;输入重载须先读临时变量再赋值,避免部分更新;模板类需额外模板化友元声明。

关键内容

1. 标准签名

std::ostream& operator<<(std::ostream& os, const MyType& obj);  // 输出:const T&
std::istream& operator>>(std::istream& is, MyType& obj);        // 输入:T&(非 const)

2. friend 声明模式

类成员为私有时,将运算符声明为 friend:

class Person {
    std::string name;
    int age;
public:
    friend std::ostream& operator<<(std::ostream& os, const Person& p);
    friend std::istream& operator>>(std::istream& is, Person& p);
};

不想用 friend 可暴露 getter/setter,但需要类提供足够的公共接口。

3. 输入安全模式:先读临时变量

直接写入对象成员会导致"读坏一半"问题(读取中途失败时对象处于中间状态):

// 不安全
istream& operator>>(istream& is, Person& p) { is >> p.name >> p.age; return is; }

// 安全:先读临时变量,成功后再赋值
istream& operator>>(istream& is, Person& p) {
    std::string name; int age;
    if (is >> name >> age) { p.name = name; p.age = age; }
    return is;
}

4. 格式校验与 failbit

需要严格输入格式时,校验失败后调用 is.setstate(std::ios::failbit) 通知调用者:

istream& operator>>(istream& is, Point& p) {
    int x, y; char c1, c2, c3;
    if (is >> c1 >> x >> c2 >> y >> c3) {
        if (c1 == '(' && c2 == ',' && c3 == ')')
            p.x = x, p.y = y;
        else
            is.setstate(std::ios::failbit);
    }
    return is;
}

5. 含空格字符串:结合 getline

operator&gt;&gt; 默认按空白分隔,读取含空格的字符串需用 std::getline

istream& operator>>(istream& is, Person& p) {
    std::getline(is >> std::ws, p.name);  // 跳过前导空白后读整行
    is >> p.age;
    return is;
}

6. 模板类重载

模板类需用模板化友元声明(避免只友好特定实例化):

template<typename T>
class Box {
    T value;
public:
    template<typename U>
    friend std::ostream& operator<<(std::ostream& os, const Box<U>& box);
    template<typename U>
    friend std::istream& operator>>(std::istream& is, Box<U>& box);
};

7. 成员函数版本的适用场景

仅当左操作数是自己的类对象时才写成员函数(如自定义 Buffer 类):

class Buffer {
public:
    Buffer& operator<<(int x) { /* write to buffer */ return *this; }
};

这与 std::cout << obj 场景完全不同。

8. 常见错误

错误 后果
operator&lt;&lt; 写成成员函数 obj.operator&lt;&lt;(cout) 语义反转
返回 void 无法链式调用
输出参数不加 const 无法输出 const 对象或临时对象
输入参数传值(非引用) 修改副本,原对象不变
输入直接写对象成员 失败时对象处于中间状态

来源

相关