由于无法用一个数来表示矢量,因此应该创建一个类来表示矢量。矢量的相加和相减与普通数学运算有相似之处,所以应重载运算符使之能用于矢量。
本节将实现一个二维矢量,描述二维矢量需要有两个数,但有两种方式:
- 用长度和角度表示(极坐标);用水平分量 x 和垂直分量 y 表示(直角坐标)。
#ifndef VECTOR_H_ #define VECTOR_H_ #includenamespace VECTOR { class Vector { public: enum Mode { RECT, // 直角坐标系 POL // 极坐标系 }; private: double x; // 水平坐标 double y; // 垂直坐标 double ang; // 角度 double mag; // 长度 Mode mode; // 采用的坐标系 // 设置私有成员的函数 void setMag(); // 根据 x, y 设置 mag void setAng(); // 根据 x, y 设置 ang void setX(); // 根据 mag, ang 设置 x void setY(); // 根据 mag, ang 设置 y public: Vector(); // 构造函数, 默认采用直角坐标系表示法 Vector(double n1, double n2, Mode form = RECT); ~Vector(); // 重置 Vector 值, 默认采用直角坐标系表示法 void reset(double n1, double n2, Mode form = RECT); double getXVal() const { return x; } double getYVal() const { return y; } double getAngVal() const { return ang; } double getMagVal() const { return mag; } void setPolarMode(); void setRectMode(); // 重载运算符 —— 成员函数 Vector operator+(const Vector & vec) const; Vector operator-(const Vector & vec) const; Vector operator-() const; Vector operator*(double d) const; // 重载运算符 —— 友元函数 friend Vector operator*(double d, const Vector & vec); friend std::ostream & operator<<(std::ostream & out, const Vector & vec); }; } // end namespace VECTOR #endif // VECTOR_H_
#include "vector.h" #includenamespace VECTOR { using std::sqrt; using std::sin; using std::cos; using std::atan; using std::atan2; using std::cout; const double RadToDeg = 45.0 / atan(1.0); // 计算一弧度对应的角度 void Vector::setAng() { // 这里其实不应该用 == 的, double 类型的 == 并不准确 if (x == 0.0 && y == 0.0) ang = 0.0; else ang = atan2(y, x); } void Vector::setMag() { mag = sqrt(x * x + y * y); } void Vector::setX() { x = mag * cos(ang); } void Vector::setY() { y = mag * sin(ang); } Vector::Vector() { cout << "Default Constructor.n"; x = y = ang = mag = 0.0; mode = RECT; } Vector::Vector(double n1, double n2, Mode form) { cout << "Constructor. "; mode = form; if (form == RECT) { x = n1; y = n2; setMag(); setAng(); } else if (form == POL) { mag = n1; ang = n2 / RadToDeg; setX(); setY(); } else { cout << "构造方法 Vector() 的第三个参数是无效的. 对象的值将被设置为 0, 并且采用直角坐标系."; x = y = ang = mag = 0.0; mode = RECT; } cout << std::endl; } Vector::~Vector() { cout << "Destructor.n"; } // 重新设置 vector 的值 void Vector::reset(double n1, double n2, Mode form) { mode = form; if (mode == RECT) { x = n1; y = n2; setAng(); setMag(); } else if (mode == POL) { mag = n1; ang = n2 /RadToDeg; setX(); setY(); } else { cout << "reset() 的第三个参数是无效的. 对象的值将被设置为 0, 并且采用直角坐标系.n"; x = y = ang = mag = 0.0; mode = RECT; } } void Vector::setPolarMode() { mode = RECT; } void Vector::setRectMode() { mode = POL; } Vector Vector::operator+(const Vector &vec) const { return Vector(x + vec.x, y + vec.y, RECT); } Vector Vector::operator-(const Vector &vec) const { return Vector(x - vec.x, y - vec.y, RECT); } Vector Vector::operator-() const { return Vector(-x, -y, RECT); } Vector Vector::operator*(double d) const { return Vector(x * d, y * d, RECT); } Vector operator*(double d, const Vector & vec) { return vec * d; } // 如果 mode 是 RECT, 显示直角坐标系的坐标; // 如果 mode 是 POL , 显示极坐标系的坐标. std::ostream & operator<<(std::ostream & out, const Vector & vec) { if (vec.mode == Vector::RECT) { out << "(x, y) = (" << vec.x << ", " << vec.y << ")" << std::endl; } else if (vec.mode == Vector::POL) { out << "(mag, ang) = (" << vec.mag << ", " << vec.ang * RadToDeg << ")n"; } else { out << "Vector object mode is invalid.n"; } return out; } }
也可以以另一种方式来设计这个类。例如,在对象中只存储直角坐标系,不保存极坐标系,在需要用到极坐标系坐标时使用 getAngVal() 和 getMagVal() 来计算极坐标系。
在重载的 +、-、* 运算符的定义中是通过类构造方法来实现的。如果方法通过计算得到一个新的类对象,则应该考虑是否可以使用类构造函数来完成这种工作。这样不仅可以避免麻烦,而且可以确保新的对象是按照正确的方式创建的。
随机漫步问题using VECTOR::Vector;
using std::cout;
using std::cin;
using std::endl;
srand(time(0)); // 设置种子
Vector result(0, 0, VECTOR::Vector::RECT); // 采用直角坐标系记录当前所在位置
Vector step; // 记录当前随机的矢量
unsigned long steps = 0; // 记录走过的总步数
double target; // 记录目标距离
double dstep; // 记录每一步的步长
cout << "请输入目标距离(按 q 表示退出):";
while (cin >> target) {
cout << "请输入步长:";
if (!(cin >> dstep)) {
cout << "输入的步长无效,程序将退出.";
break;
}
// 如果距离没有达到目标距离, 继续走下一步
while (result.getMagVal() < target) {
// 随机生成下一步的方向, 并利用步长和方向按照极坐标系创建实例
step = Vector(dstep, rand() % 360, Vector::POL);
// 往 step 指示方向位移一步
result = result + step;
// 走的步数+1
steps++;
}
cout << "经过 " << steps << " 步之后到达指定的距离, 当前位置如下所示: " << endl;
cout << "(x, y) = (" << result.getXVal() << ", " << result.getYVal()
<< ") or (mag, ang) = (" << result.getMagVal() << ", " << result.getAngVal()
<< ")n";
cout << "移动的平均步长为: " << result.getMagVal() / steps << endl;
// 重置步数和当前所在位置
steps = 0;
result.reset(0, 0, Vector::RECT);
cout << "请输入目标距离(按 q 表示退出):";
}
cout << "Bye!n";
cin.clear();
time(0) 返回当前系统时间,srand(time(0)) 将当前系统时间作为种子值,rand() 函数将根据 srand() 设置的种子值返回一个伪随机数。
该程序使用 result 记录行走者当前的位置情况。内循环每轮将 step 矢量设置新的方向,并将它与当前的 result 矢量相加。当 result 的长度超过指定的距离之后,该循环结束。
result = result + step;这条语句将 result 设置为 RECT 模式,而不管 result 和 step 的初始模式是什么,这是在 + 运算符重载的实现决定的,如果不希望这样实现,可以修改 operator+() 方法的实现。
如果想要将行走者一系列的位置存储到文件中也是很容易的,只需要包含 fstream 头文件,并声明一个 ofstream 对象,将其同一个文件关联起来,然后使用 << 运算符将位置记录输出到文件中:
#include... ofstream fout; fout.open("walker.txt"); fout << result << endl;



