当前位置:首页 > 技术 > 正文内容

C++ 重载流插入和流提取运算符

访客 技术 2026年6月19日 1

C++ 中,标准输出流对象 std::cout 和输入流对象 std::cin 分别通过重载的流插入运算符 (<<) 和流提取运算符 (>>) 来处理基本数据类型的输出和输入。

流插入 (<<) 运算符的重载

当我们在代码中使用 std::cout << 1 << "hello"; 这样的语句时,可能会产生疑问:cout 是什么?为什么 << 运算符能用于 cout

实际上,cout<iostream> 头文件中定义的 ostream 类的实例。<< 运算符之所以能用于 cout,是因为 ostream 类已经对该运算符进行了重载。

对于语句 std::cout << 1 << "hello";,其执行过程可以理解为 ostream 类中的成员函数被依次调用:

ostream& ostream::operator<<(int num) {
    // 输出整数 num 的逻辑
    return *this; // 返回流对象本身,实现链式调用
}

ostream& ostream::operator<<(const char* str) {
    // 输出字符串 str 的逻辑
    return *this; // 返回流对象本身,实现链式调用
}
    

因此,std::cout << 1; 等价于 cout.operator<<(1);,而 std::cout << 1 << "hello"; 则等价于 (cout.operator<<(1)).operator<<("hello");

为自定义类重载流插入运算符

如果我们希望能够直接输出自定义对象的内容,就需要为该对象重载 ostream 类的流插入 << 运算符。

以下以一个名为 Student 的类为例,演示如何重载 << 运算符:

#include <iostream>
#include <string>

class Student {
public:
    // 构造函数
    Student(int id = 0, int age = 0, std::string name = "") : m_id(id), m_age(age), m_name(name) {}

    // 声明为友元函数,以便访问私有成员
    friend std::ostream& operator<<(std::ostream& os, const Student& s);

private:
    int m_age;      // 年龄
    int m_id;       // ID
    std::string m_name; // 姓名
};

// 重载 ostream 的流插入运算符
std::ostream& operator<<(std::ostream& os, const Student& s) {
    os << "ID: " << s.m_id << ", Age: " << s.m_age << ", Name: " << s.m_name;
    return os; // 返回流对象,支持链式操作
}

int main() {
    Student student1(101, 22, "Alice");
    std::cout << student1 << std::endl; // 直接输出 Student 对象
    return 0;
}
    

输出结果:

ID: 101, Age: 22, Name: Alice
    

需要注意的是,重载的 operator<< 函数通常是全局函数。其第一个参数必须是 ostream 对象的引用,第二个参数是要输出的自定义对象(通常是常量引用)。同时,为了让该全局函数能够访问类的私有成员,需要在类定义中将其声明为 friend

流提取 (>>) 运算符的重载

类似地,如果我们希望通过输入流(如 std::cin)来初始化自定义对象,就需要重载 istream 类的流提取 >> 运算符。

以下继续以 Student 类为例,演示如何重载 >> 运算符:

#include <iostream>
#include <string>
#include <sstream> // 用于字符串解析

class Student {
public:
    // 构造函数
    Student(int id = 0, int age = 0, std::string name = "") : m_id(id), m_age(age), m_name(name) {}

    // 声明为友元函数,以便访问私有成员
    friend std::ostream& operator<<(std::ostream& os, const Student& s);
    friend std::istream& operator>>(std::istream& is, Student& s); // 注意这里是 Student&, 因为需要修改对象

private:
    int m_age;      // 年龄
    int m_id;       // ID
    std::string m_name; // 姓名
};

// 重载 ostream 的流插入运算符 (同上)
std::ostream& operator<<(std::ostream& os, const Student& s) {
    os << "ID: " << s.m_id << ", Age: " << s.m_age << ", Name: " << s.m_name;
    return os;
}

// 重载 istream 的流提取运算符
std::istream& operator>>(std::istream& is, Student& s) {
    std::string line;
    is >> line; // 读取一行输入

    std::stringstream ss(line);
    std::string segment;
    
    // 解析 ID
    if (std::getline(ss, segment, ',')) {
        s.m_id = std::stoi(segment); // string to integer
    } else {
        is.setstate(std::ios::failbit); // 如果解析失败,设置流的状态
        return is;
    }

    // 解析 Age
    if (std::getline(ss, segment, ',')) {
        s.m_age = std::stoi(segment);
    } else {
        is.setstate(std::ios::failbit);
        return is;
    }

    // 解析 Name
    if (std::getline(ss, segment)) { // 最后一个字段,直接读取到末尾
        s.m_name = segment;
    } else {
        is.setstate(std::ios::failbit);
        return is;
    }

    return is; // 返回流对象,支持链式操作
}

int main() {
    Student student2;
    std::cout << "Enter student details (ID,Age,Name): ";
    std::cin >> student2; // 使用重载的 >> 运算符输入数据
    std::cout << "Entered details: " << student2 << std::endl;
    return 0;
}
    

假设用户输入 102,23,Bob,输出将是:

Enter student details (ID,Age,Name): 102,23,Bob
Entered details: ID: 102, Age: 23, Name: Bob
    

重载 >> 运算符时,第一个参数是 istream 对象的引用,第二个参数是需要被修改的自定义对象(必须是普通引用,而非常量引用)。同样,需要将其声明为类的友元函数。

总结

为了让 C++ 的流插入 << 和流提取 >> 运算符能够处理自定义对象,我们需要为这些对象重载相应的 ostream::operator<<istream::operator>> 函数。这些重载函数通常定义为全局函数,并且需要在类定义中声明为友元函数,以便能够访问和修改类的私有成员。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。