
下面详细讲解 C 中运算符的重载并给出一个完整可运行的程序。一、的本质与重载意义在 C 中原本是位右移运算符bitwise right shift例如cppint a 16 2; // a 4但 C 允许运算符重载被标准库选作流提取运算符stream extraction operator用于从输入流读取数据cppint x; std::cin x;这里的含义已不再是右移而是从左侧的流中提取数据到右侧的变量。二、为什么适合做输入运算符的视觉方向与相反数据从左侧的流流入右侧的变量。用于输出数据流向流用于输入数据从流流出到变量两者形成对称代码读起来自然。三、重载的两种方式方式一成员函数不推荐用于输入cppclass Point { public: std::istream operator(std::istream is) { is x_ y_; return is; } private: int x_, y_; };调用时必须写成p std::cin完全违背直觉也无法与std::cin p兼容。所以输入运算符几乎总是重载为非成员函数。方式二非成员函数推荐cppstd::istream operator(std::istream is, Point p);这样std::cin p才能正常工作。四、重载的关键要点要点说明返回类型必须是std::istream以支持链式调用第一个参数std::istream不能是const因为读取会改变流状态第二个参数自定义类型的非 const 引用T因为要修改对象是否友元需要访问私有成员时声明为friend否则可用公有 setter不要定义为成员函数否则左操作数必须是自定义类无法配合std::cin错误处理读取失败时应设置流的失败状态而不是让对象处于半初始化状态与重载的关键区别的第二个参数是const T只读而的第二个参数是T非 const因为输入操作需要修改对象。这是两者最重要的区别。下面详细讲解 C 中运算符的重载并给出一个完整可运行的程序。一、的本质与重载意义在 C 中原本是位右移运算符bitwise right shift例如cppint a 16 2; // a 4但 C 允许运算符重载被标准库选作流提取运算符stream extraction operator用于从输入流读取数据cppint x; std::cin x;这里的含义已不再是右移而是从左侧的流中提取数据到右侧的变量。二、为什么适合做输入运算符的视觉方向与相反数据从左侧的流流入右侧的变量。用于输出数据流向流用于输入数据从流流出到变量两者形成对称代码读起来自然。三、重载的两种方式方式一成员函数不推荐用于输入cppclass Point { public: std::istream operator(std::istream is) { is x_ y_; return is; } private: int x_, y_; };调用时必须写成p std::cin完全违背直觉也无法与std::cin p兼容。所以输入运算符几乎总是重载为非成员函数。方式二非成员函数推荐cppstd::istream operator(std::istream is, Point p);这样std::cin p才能正常工作。四、重载的关键要点要点说明返回类型必须是std::istream以支持链式调用第一个参数std::istream不能是const因为读取会改变流状态第二个参数自定义类型的非 const 引用T因为要修改对象是否友元需要访问私有成员时声明为friend否则可用公有 setter不要定义为成员函数否则左操作数必须是自定义类无法配合std::cin错误处理读取失败时应设置流的失败状态而不是让对象处于半初始化状态与重载的关键区别的第二个参数是const T只读而的第二个参数是T非 const因为输入操作需要修改对象。这是两者最重要的区别。// demo_input.cpp // 编译: g -stdc17 demo_input.cpp -o demo_input // 运行: ./demo_input #include iostream #include string #include vector #include sstream #include limits // // 1. 普通类Point使用友元重载 // class Point { public: Point(int x 0, int y 0) : x_(x), y_(y) {} int x() const { return x_; } int y() const { return y_; } // 友元声明 friend std::ostream operator(std::ostream os, const Point p); friend std::istream operator(std::istream is, Point p); private: int x_, y_; }; // 输出运算符配套使用 std::ostream operator(std::ostream os, const Point p) { os ( p.x_ , p.y_ ); return os; } // 输入运算符读取形如 3 4 或 (3, 4) 的输入 std::istream operator(std::istream is, Point p) { // 尝试读取 (x, y) 格式 char ch; if (is ch ch () { int x, y; char comma; if (is x comma y ch comma , ch )) { p.x_ x; p.y_ y; return is; } is.setstate(std::ios::failbit); // 格式错误设置失败位 return is; } // 回退按 x y 格式读取 is.unget(); // 把读到的字符放回流中 int x, y; if (is x y) { p.x_ x; p.y_ y; } return is; } // // 2. 带错误处理的输入Range 类限定取值范围 // class Range { public: Range(int lo 0, int hi 100) : lo_(lo), hi_(hi), value_(lo) {} int value() const { return value_; } friend std::istream operator(std::istream is, Range r); private: int lo_, hi_, value_; }; // 输入时检查范围超范围则设置 failbit std::istream operator(std::istream is, Range r) { int v; if (!(is v)) { return is; // 读取失败直接返回 } if (v r.lo_ || v r.hi_) { is.setstate(std::ios::failbit); // 超出范围标记失败 return is; } r.value_ v; return is; } // // 3. 模板类MyVectorT演示模板重载 // template typename T class MyVector { public: MyVector() default; void push_back(const T v) { data_.push_back(v); } size_t size() const { return data_.size(); } template typename U friend std::ostream operator(std::ostream os, const MyVectorU v); template typename U friend std::istream operator(std::istream is, MyVectorU v); private: std::vectorT data_; }; template typename U std::ostream operator(std::ostream os, const MyVectorU v) { os [; for (size_t i 0; i v.data_.size(); i) { if (i 0) os , ; os v.data_[i]; } os ]; return os; } // 输入格式先读数量 n再读 n 个元素 template typename U std::istream operator(std::istream is, MyVectorU v) { size_t n; if (!(is n)) { return is; } v.data_.clear(); v.data_.reserve(n); for (size_t i 0; i n; i) { U item; if (!(is item)) { is.setstate(std::ios::failbit); return is; } v.data_.push_back(item); } return is; } // // 4. 继承Shape 基类 Circle / Rectangle // class Shape { public: virtual ~Shape() default; virtual std::string name() const 0; virtual double area() const 0; }; std::ostream operator(std::ostream os, const Shape s) { os s.name() (area s.area() ); return os; } class Circle : public Shape { public: Circle(double r 0) : r_(r) {} std::string name() const override { return Circle; } double area() const override { return 3.14159 * r_ * r_; } friend std::istream operator(std::istream is, Circle c); private: double r_; }; std::istream operator(std::istream is, Circle c) { double r; if (is r r 0) { c.r_ r; } else { is.setstate(std::ios::failbit); } return is; } class Rectangle : public Shape { public: Rectangle(double w 0, double h 0) : w_(w), h_(h) {} std::string name() const override { return Rectangle; } double area() const override { return w_ * h_; } friend std::istream operator(std::istream is, Rectangle r); private: double w_, h_; }; std::istream operator(std::istream is, Rectangle r) { double w, h; if (is w h w 0 h 0) { r.w_ w; r.h_ h; } else { is.setstate(std::ios::failbit); } return is; } // // main演示各种用法 // int main() { // ---- 1. 普通类 Point ---- std::cout 1. 普通类 Point \n; { std::istringstream iss((3, 4) 10 20); Point p1, p2; iss p1 p2; std::cout p1 p1 \n; std::cout p2 p2 \n; } // ---- 2. 带错误处理 Range ---- std::cout \n 2. 带错误处理 Range \n; { std::istringstream iss(50 200); Range r1(0, 100), r2(0, 100); iss r1; std::cout r1 r1.value() (读取成功)\n; iss r2; if (iss.fail()) { std::cout r2 读取失败值超出 [0, 100] 范围\n; } } // ---- 3. 模板类 MyVector ---- std::cout \n 3. 模板类 MyVector \n; { std::istringstream iss(3 1 2 3 2 4 5); MyVectorint vi; MyVectorint vi2; iss vi vi2; std::cout vi vi \n; std::cout vi2 vi2 \n; } // ---- 4. 继承与多态 ---- std::cout \n 4. 继承与多态 \n; { std::istringstream iss(2.0 3.0 4.0); Circle c; Rectangle r; iss c r; std::cout c c \n; std::cout r r \n; const Shape s1 c; const Shape s2 r; std::cout s1 s1 \n; std::cout s2 s2 \n; } // ---- 5. 实际从 std::cin 读取 ---- std::cout \n 5. 交互式输入 \n; std::cout 请输入一个点 (格式: x y): ; Point p; if (std::cin p) { std::cout 你输入的点是: p \n; } else { std::cout 输入格式错误\n; std::cin.clear(); std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); } return 0; }