C++ 重载流插入和流提取运算符
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>> 函数。这些重载函数通常定义为全局函数,并且需要在类定义中声明为友元函数,以便能够访问和修改类的私有成员。