C++ —— 重载运算符练习: 矢量类

练习代码

由于无法用一个数来表示矢量,因此应该创建一个类来表示矢量。矢量的相加和相减与普通数学运算有相似之处,所以应重载运算符使之能用于矢量。
本节将实现一个二维矢量,描述二维矢量需要有两个数,但有两种方式:

  1. 用长度和角度表示(极坐标);
  2. 用水平分量 x 和垂直分量 y 表示(直角坐标)。
#ifndef VECTOR_H_
#define VECTOR_H_
#include <iostream>
namespace 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"
#include <cmath>
namespace 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); // 计算一弧度对应的角度

    /**
     * Private methods
     */
    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);
    }

    /**
     * Public methods —— Constructor And Destructor
     */
    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";
    }

    /**
     * Public Function.
     */
     // 重新设置 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; }

    /**
     * Operator overload —— Member Function
     */
    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);
    }
    /**
     * Operator overload —— Friend Function
     */
    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 <fstream>
...
ofstream fout;
fout.open("walker.txt");
fout << result << endl;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值