C++ 中流插入(<<
)与提取(>>
)运算符重载解析
一、运算符重载的基本规则
-
运算符本质
<<
和>>
是二元运算符,左操作数为流对象(istream
/ostream
),右操作数为自定义类型对象。例如:cout << obj
实际调用的是operator<<(cout, obj)
,cin >> obj
调用的是operator>>(cin, obj)
。 -
必须重载为友元函数
由于左操作数是流对象(非类成员),需将运算符重载函数声明为类的 友元函数,以便访问类的私有成员。 -
返回流对象的引用
返回流对象的引用(ostream&
或istream&
)是为了支持链式调用,例如cout << a << b
。
二、流插入运算符 <<
重载
1. 语法格式
// 声明为类的友元函数
friend ostream& operator<<(ostream& os, const MyClass& obj);
// 函数实现
ostream& operator<<(ostream& os, const MyClass& obj) {
os << obj.data1 << ", " << obj.data2; // 按需访问和输出成员
return os;
}
2. 示例:输出学生信息
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
string name;
int age;
public:
Student(string n, int a) : name(n), age(a) {}
// 声明为友元函数
friend ostream& operator<<(ostream& os, const Student& s);
};
// 实现 << 运算符重载
ostream& operator<<(ostream& os, const Student& s) {
os << "Name: " << s.name << ", Age: " << s.age;
return os;
}
int main() {
Student s("Alice", 20);
cout << s; // 输出:Name: Alice, Age: 20
return 0;
}
三、流提取运算符 >>
重载
1. 语法格式
// 声明为类的友元函数
friend istream& operator>>(istream& is, MyClass& obj);
// 函数实现
istream& operator>>(istream& is, MyClass& obj) {
is >> obj.data1 >> obj.data2; // 按需读取数据
return is;
}
2. 示例:输入学生信息
class Student {
// 假设需要以下成员
string name;
int age;
public:
Student() {}
// 声明友元函数
friend istream& operator>>(istream& is, Student& s);
};
// 实现 >> 运算符重载
istream& operator>>(istream& is, Student& s) {
cout << "Enter name and age: ";
is >> s.name >> s.age; // 读取数据
return is;
}
int main() {
Student s;
cin >> s; // 输入数据到 s
cout << s; // 输出(假设 << 已重载)
return 0;
}
四、关键注意事项
-
输入参数必须为非
const
引用
因为>>
运算符需要修改对象状态。istream& operator>>(istream& is, Student& s); // 正确
-
处理输入错误
在>>
重载中应检查输入合法性,例如年龄不能为负数:if (!(is >> s.age)) { cerr << "Invalid input!"; is.setstate(ios::failbit); }
-
链式调用的实现
返回流引用允许连续操作:Student s1, s2; cin >> s1 >> s2;
-
避免修改流状态
不要随意更改流的格式(如hex
、setw
),以免影响后续操作。
五、综合示例:日期类输入输出
#include <iostream>
using namespace std;
class Date {
private:
int year, month, day;
public:
Date(int y=0, int m=0, int d=0) : year(y), month(m), day(d) {}
friend ostream& operator<<(ostream& os, const Date& d);
friend istream& operator>>(istream& is, Date& d);
};
// 输出运算符重载
ostream& operator<<(ostream& os, const Date& d) {
os << d.year << "-" << d.month << "-" << d.day;
return os;
}
// 输入运算符重载
istream& operator>>(istream& is, Date& d) {
char dash1, dash2;
is >> d.year >> dash1 >> d.month >> dash2 >> d.day;
// 验证格式
if (dash1 != '-' || dash2 != '-' || d.month < 1 || d.month > 12) {
is.setstate(ios::failbit);
}
return is;
}
int main() {
Date d;
cout << "Enter date (YYYY-MM-DD): ";
cin >> d;
if (cin.fail()) {
cerr << "Invalid date format!";
} else {
cout << "Date: " << d << endl; // 输出日期
}
return 0;
}
六、总结
- 友元声明:必须将运算符重载函数声明为类的友元。
- 返回流引用:确保支持链式调用。
- 输入验证:在
>>
重载中处理非法输入。 - 参数类型:
<<
的第二个参数为const
引用,>>
的第二个参数为非const
引用。