题目
编写程序,设计表示二维几何向量的类 Vector2D。演示为什么流输出运算符 operator<< 与输入运算符 operator>> 绝对不能被声明为成员函数(因为左操作数是 std::ostream 或 std::istream),而必须声明为全局友元函数;重点掌握返回流引用 std::ostream& 以实现链式连续输出(std::cout << a << b;),以及在 operator>> 中对非法输入格式时的 is.setstate(std::ios::failbit) 流错误状态置位防御。
输入格式:无需外部输入(内置字符串流模拟测试)。
输出格式:输出格式化流连续输出与从流中解析出 Vector2D 的回显日志。
数据范围与约定:I/O 流重载规范、链式调用机制与流状态错误标定。
思路与算法
1. 为什么只能是非成员函数
如果将 operator<< 重载为类成员函数,调用语句将被迫写成 vec << std::cout;(即 vec.operator<<(std::cout)),违背常规认知。为了让 std::cout << vec 成立,左操作数必须是 ostream&,因此必须是全局非成员函数。
2. 链式连续调用返回 ostream&
连续输出 cout << v1 << v2; 执行过程为 (cout << v1) << v2;。因此重载函数必须返回传入的 os 引用,以支撑后续操作符。
3. 输入流容错防御
若输入格式与预期符号(如括号、逗号)不符,必须主动调用 is.setstate(std::ios::failbit); 标记流损坏,避免给对象填入未定义脏数据。
完整程序
#include <iostream>
#include <sstream>
class Vector2D {
private:
double x_;
double y_;
public:
Vector2D(double x = 0.0, double y = 0.0) : x_(x), y_(y) {}
// 1. 流输出运算符重载(全局友元函数)
friend std::ostream& operator<<(std::ostream& os, const Vector2D& vec) {
os << "(" << vec.x_ << ", " << vec.y_ << ")";
return os; // 返回流引用以支持链式连续调用
}
// 2. 流输入运算符重载(读取格式如:(3.5, 4.2))
friend std::istream& operator>>(std::istream& is, Vector2D& vec) {
char ch1 = 0, ch2 = 0, ch3 = 0;
double x = 0.0, y = 0.0;
if (is >> ch1 >> x >> ch2 >> y >> ch3) {
if (ch1 == '(' && ch2 == ',' && ch3 == ')') {
vec.x_ = x;
vec.y_ = y;
} else {
is.setstate(std::ios::failbit); // 格式不匹配置 failbit
}
}
return is;
}
};
int main() {
Vector2D v1(10.5, 20.8);
Vector2D v2(-3.0, 7.5);
std::cout << "--- 1. 链式连续流输出 ---\n";
std::cout << "向量 v1: " << v1 << ", 向量 v2: " << v2 << "\n\n";
std::cout << "--- 2. 从字符串流解析向量输入 ---\n";
std::string streamData = "(100.2, 500.8)";
std::stringstream ss(streamData);
Vector2D v3;
if (ss >> v3) {
std::cout << "成功从字符串解析得到 v3: " << v3 << "\n";
} else {
std::cout << "解析输入格式错误!\n";
}
return 0;
}运行示例
输出:
--- 1. 链式连续流输出 ---
向量 v1: (10.5, 20.8), 向量 v2: (-3, 7.5)
--- 2. 从字符串流解析向量输入 ---
成功从字符串解析得到 v3: (100.2, 500.8)