C++中左移运算符重载

在 C++ 中,左移运算符(<<) 最常用的场景是配合 cout 输出数据(如 cout << "hello")。对于自定义类型(如类、结构体),默认无法直接用 cout << 对象 输出,需通过重载 << 运算符实现,这也是 << 运算符重载最核心的应用场景。

一、左移运算符重载的核心特点

1、<< 是双目运算符:左操作数是 ostream 对象(如 cout),右操作数是自定义类型对象;

2、必须重载为全局函数 / 友元:若重载为成员函数,左操作数会被绑定为当前类对象(如 obj << cout),违反 cout << obj 的使用直觉,因此只能用全局函数,且需声明为类的友元(以访问私有成员);

3、返回值为 ostream&:支持链式输出(如 cout << obj1 << obj2 << endl);

4、不修改操作数:通常用 const 修饰自定义类型参数,保证不修改对象。

二、重载语法与步骤

  1. 核心语法
cpp 复制代码
// 类内声明友元(需访问私有成员时)
friend ostream& operator<<(ostream& os, const 类名& obj);

// 全局定义重载函数
ostream& operator<<(ostream& os, const 类名& obj) {
    // 自定义输出逻辑(如输出对象的成员)
    os << 成员1 << " " << 成员2; 
    return os; // 返回ostream对象,支持链式输出
}
  1. 关键说明
    ostream& 返回值:返回传入的 os 引用(而非临时对象),确保链式调用时 cout 是同一个对象;
    const 类名& obj:用引用避免拷贝,const 保证不修改对象(即使输出常量对象也能生效);
    友元声明:仅当需要访问类的私有 / 保护成员时才需要,若成员是 public,可省略友元,直接通过公共接口访问。
    三、基础示例:自定义类输出
    以复数类为例,实现 cout << 复数对象 输出实部和虚部:
cpp 复制代码
#include <iostream>
using namespace std;

class Complex {
private:
    double real; // 实部(私有)
    double imag; // 虚部(私有)
public:
    // 构造函数
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

    // 声明 << 运算符为友元(访问私有成员)
    friend ostream& operator<<(ostream& os, const Complex& c);
};

// 全局定义左移运算符重载
ostream& operator<<(ostream& os, const Complex& c) {
    // 自定义输出格式
    os << c.real << " + " << c.imag << "i";
    return os; // 返回ostream引用,支持链式输出
}

int main() {
    Complex c1(1.5, 2.5), c2(3.5, 4.5);
    
    // 基础输出
    cout << "c1: " << c1 << endl; // 输出:c1: 1.5 + 2.5i
    
    // 链式输出
    cout << "c1 = " << c1 << ", c2 = " << c2 << endl; // 输出:c1 = 1.5 + 2.5i, c2 = 3.5 + 4.5i
    
    return 0;
}

四、进阶示例:支持多格式输出 + 非友元重载

若不想用友元(避免破坏封装),可通过公共接口(getter 函数)访问成员,实现非友元重载:

cpp 复制代码
#include <iostream>
using namespace std;

class Student {
private:
    string name;
    int age;
    double score;
public:
    Student(string n, int a, double s) : name(n), age(a), score(s) {}

    // 公共getter接口
    string getName() const { return name; }
    int getAge() const { return age; }
    double getScore() const { return score; }
};

// 非友元重载 <<:通过getter访问成员
ostream& operator<<(ostream& os, const Student& stu) {
    os << "姓名:" << stu.getName() 
       << ",年龄:" << stu.getAge() 
       << ",成绩:" << stu.getScore();
    return os;
}

int main() {
    Student stu("张三", 18, 95.5);
    cout << stu << endl; // 输出:姓名:张三,年龄:18,成绩:95.5
    return 0;
}

五、支持自定义输出流(如文件流)

<< 重载不仅支持 cout(控制台输出),还支持所有 ostream 派生类(如 ofstream 文件输出),因为重载函数的参数是基类 ostream&:

cpp 复制代码
#include <iostream>
#include <fstream>
using namespace std;

class Complex {
private:
    double real, imag;
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    friend ostream& operator<<(ostream& os, const Complex& c);
};

ostream& operator<<(ostream& os, const Complex& c) {
    os << c.real << " + " << c.imag << "i";
    return os;
}

int main() {
    Complex c(2.5, 3.5);
    
    // 输出到控制台
    cout << "控制台输出:" << c << endl;
    
    // 输出到文件
    ofstream fout("complex.txt");
    if (fout.is_open()) {
        fout << "文件输出:" << c << endl;
        fout.close();
    }
    
    return 0;
}
运行后,complex.txt 文件中会写入:文件输出:2.5 + 3.5i。

六、常见误区与注意事项

1、错误地重载为成员函数:

cpp 复制代码
// 错误示例:成员函数重载 <<,左操作数是Complex对象,使用时需写 c << cout,违反直觉
class Complex {
public:
    ostream& operator<<(ostream& os) {
        os << real << " + " << imag << "i";
        return os;
    }
};
// 调用:c1 << cout; // 极其反人类,绝对避免!

2、返回值错误:

若返回 void,无法支持链式输出(如 cout << c1 << c2 会报错);

若返回 ostream(值返回),会拷贝 cout(ostream 不支持拷贝),导致编译错误,必须返回 ostream&(引用)。

3、未用 const 修饰对象参数:

若参数是 Complex& c(无 const),则无法输出常量对象(如 const Complex c(1,2); cout << c; 报错),必须加 const。

4、友元的滥用:

仅当需要访问私有成员时才声明友元,优先通过 getter 接口实现非友元重载,减少封装破坏。

七、与右移运算符(>>)重载的配合

左移(<<)用于输出,右移(>>)用于输入,通常成对重载,示例如下:

cpp 复制代码
#include <iostream>
using namespace std;

class Complex {
private:
    double real, imag;
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    // 重载 <<(输出)
    friend ostream& operator<<(ostream& os, const Complex& c);
    // 重载 >>(输入)
    friend istream& operator>>(istream& is, Complex& c);
};

ostream& operator<<(ostream& os, const Complex& c) {
    os << c.real << " + " << c.imag << "i";
    return os;
}

istream& operator>>(istream& is, Complex& c) {
    // 自定义输入格式:输入两个数,分别赋值给实部和虚部
    cout << "请输入实部和虚部(空格分隔):";
    is >> c.real >> c.imag;
    return is;
}

int main() {
    Complex c;
    cin >> c; // 输入:3 4
    cout << "你输入的复数是:" << c << endl; // 输出:3 + 4i
    return 0;
}

总结

左移运算符(<<)重载的核心目的是让自定义类型支持 cout(或其他 ostream)输出;

必须重载为全局函数(友元按需声明),返回 ostream& 以支持链式输出;

参数需用 const 类名& 修饰,保证不修改对象且避免拷贝;

可兼容所有 ostream 派生类(控制台、文件等),通用性强;

通常与 >> 运算符重载配合,实现自定义类型的输入输出。

相关推荐
CoderCodingNo2 小时前
【GESP】C++五级真题(数论-素数、贪心思想考点) luogu-B4050 [GESP202409 五级] 挑战怪物
开发语言·c++·算法
~光~~2 小时前
【记录——内核模块加载到内核】基于鲁班猫4 rk3588s
c++·学习·rk3588s
Kiyra2 小时前
LinkedHashMap 源码阅读
java·开发语言·网络·人工智能·安全·阿里云·云计算
沐知全栈开发2 小时前
Python3 日期和时间处理详解
开发语言
老王熬夜敲代码2 小时前
C++模版元编程2
开发语言·c++
2501_916766542 小时前
【Java】HashMap集合实现类
java·开发语言
hope_wisdom2 小时前
C/C++数据结构之队列基础
c语言·数据结构·c++·队列·queue
海棠AI实验室2 小时前
Python 学习路线图:从 0 到 1 的最短闭环
开发语言·python·学习
图形学爱好者_Wu2 小时前
C++ 数据结构 | 数组的底层原理
c++