第11章 使用类——再谈重载:矢量类(一)

本文章是作者根据史蒂芬·普拉达所著的《C++ Primer Plus》而整理出的读书笔记,如果您在浏览过程中发现了什么错误,烦请告知。另外,此书由浅入深,非常适合有C语言基础的人学习,感兴趣的朋友可以自行阅读此书籍。

矢量,是工程和物理中使用的一个术语,它是一个有大小和方向的量。例如,推东西时,推的效果将取决于推力的大小和推的方向。
同时,矢量也支持相加,首先,画一个矢量,然后从第一个矢量的尾部开始画第二个矢量。最后从第一个矢量的开始处向第二个矢量的结尾处画一个矢量。第三个矢量表示前两个矢量的和。注意,两个矢量之和的长度可能小于它们的长度之和。

显然无法使用一个数来表示矢量,因此应创建一个类来表示矢量。其次,矢量与普通数学运算有相似之处,因此应重载运算符。

出于简化的目的,本节将实现一个二维矢量(如屏幕位移)。描述二维矢量只需要两个数,但可以选择到底使用哪两个数:

  • 可以用大小(长度)和方向(角度)描述矢量;
  • 可以用分量x和y表示矢量。

两个分量分别是水平矢量(x分量)和垂直矢量(y分量),将其相加可以得到最终的矢量。例如,可以这样描述点的运动:向右移动30个单位,再向上移动40个单位。这将把该点沿与水平方向呈53.1度的方向移动50个单位(没错,就是勾股定理),因此,水平分量为30个单位、垂直分量为40个单位的矢量,与长度为50个单位、方向为53.1度的矢量相同。

因此在设计这个类时,当用户修改了矢量的一种表示时,对象将自动更新另一种表示。

以下是类声明的代码:

//vect.hpp
#ifndef _VECTOR_H_
#define _VECTOR_H_
#include <iostream>
namespace VECTOR
{
  class Vector
  {
  public:
    enum Mode{RECT, POL};

  private:
    double x;
    double y;
    double mag;
    double ang;
    Mode mode;
    void set_mag();
    void set_ang();
    void set_x();
    void set_y();

  public:
    Vector();
    Vector(double n1, double n2, Mode form = RECT);
    void reset(double n1, double n2, Mode form = RECT);
    ~Vector();
    double xval() const {return x;}
    double yval() const {return x;}
    double magval() const {return mag;}
    double angval() const {return ang;}
    void polar_mode();
    void rect_mode();
    
    Vector operator+(const Vector& b) const;
    Vector operator-(const Vector& b) const;
    Vector operator-() const;
    Vector operator* (double n) const;
    
    friend Vector operator*(double n, const Vector& a);
    friend std::ostream & operator<<(std::ostream & os, const Vector& v);
  };
}//end namespace VECTOR

#endif 

以下类实现代码:

//vect.cpp
#include "cmath"
#include "vect.hpp"

using std::sqrt;
using std::sin;
using std::cos;
using std::atan;
using std::atan2;
using std::cout;
using std::endl;

namespace VECTOR
{

    const double Rad_to_deg = 45.0 / atan(1.0);

    void Vector::set_mag()
    {
      mag = sqrt(x * x + y * y);      
    }

    void Vector::set_ang()
    {
      if (x == 0.0 && y == 0.0)
        ang = 0.0;
      else
        ang = atan2(y, x);
    }

    void Vector::set_x()
    {
      x = mag * cos(ang);
    }

    void Vector::set_y()
    {
      y = mag * sin(ang);    
    }

    Vector::Vector()
    {
      x = y = mag = ang = 0.0;
      mode = RECT;
    }

    Vector::Vector(double n1, double n2, Mode form)
    {
      mode = form;
      if(form == RECT)
      {
        x = n1;
        y = n2;
        set_mag();
        set_ang();
      }
      else if (form == POL)
      {
        mag = n1;
        ang = n2 / Rad_to_deg;
        set_x();
        set_y();
      }
      else{
        cout << "Incorrect 3rd argument to Vector() --";
        cout << "vector set to 0\n";
        x = y = mag = ang = 0.0;
        mode = RECT;
      }
      
    }

    void Vector::reset(double n1, double n2, Mode form)
    {
      mode = form;
      if(form == RECT)
      {
        x = n1;
        y = n2;
        set_mag();
        set_ang();
      }
      else if (form == POL)
      {
        mag = n1;
        ang = n2 / Rad_to_deg;
        set_x();
        set_y();
      }
      else{
        cout << "Incorrect 3rd argument to Vector() --";
        cout << "vector set to 0\n";
        x = y = mag = ang = 0.0;
        mode = RECT;
      }
    }

    Vector::~Vector()
    {

    }

    void Vector::polar_mode()
    {
      mode = POL;
    }

    void Vector::rect_mode()
    {
      mode = RECT;
    }

    Vector Vector::operator+(const Vector& b) const
    {
      return Vector(x + b.x, y + b.y);            //看似只处理了直角坐标,实际上构造函数中会实时更新极坐标
    }

    Vector Vector::operator-(const Vector& b) const
    {
      return Vector(x - b.x, y - b.y);
    }

    Vector Vector::operator-() const
    {
      return Vector(-x, -y);
    }

    Vector Vector::operator* (double n) const
    {
      return Vector(n * x, n * y);
    }
    
    Vector operator*(double n, const Vector& a)
    {
      return a * n;
    }

    std::ostream & operator<<(std::ostream & os, const Vector& v)
    {
      if (v.mode == Vector::RECT)
      {
        os << "(x, y) = (" << v.x << ", " << v.y << ")";
      }
      else if(v.mode == Vector::POL)
      {
        os << "(m, a) = (" << v.mag << ", " << v.ang * Rad_to_deg << ")"; 
      }  
      else
      {
        os << "Vector object mode is invalid";
      }
      return os;
    }
}//end namespace VECTOR

可以使用Vector类来模拟随机漫步问题:

随机漫步问题,指的是,将一个人领到街灯柱下。这个人开始走动,但每一步的方向都是随机的(与前一步不同)。这个问题的一种表述是,这个人走到离灯柱50英尺处需要多少步。

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "vect.hpp"

int main()
{
    using namespace std;
    using VECTOR::Vector;

    srand(time(0));
    double direction;
    Vector step;
    Vector result(0.0, 0.0);
    unsigned long steps = 0;
    double target;
    double dstep;
    cout << "请输入目标距离(按任意字母退出): ";
    while (cin >> target)
    {
        cout << "输入步长:";
        if (!(cin >> dstep))
        {
            break;
        }

        while (result.magval() < target)
        {
            direction = rand() % 360;                                     
            step.reset(dstep, direction, Vector::POL);                    //新的一步,新的方向
            result = result + step;                                       
            steps ++ ;
        }

        cout << "走了" << steps << "步后,当前位置:\n";
        cout << result << endl;
        result.polar_mode();
        cout << "or\n" << result << endl;

        cout << "平均每步向外距离 = "
            << result.magval()/steps << endl;
        steps = 0;
        result.reset(0.0, 0.0);
        cout << "请输入目标距离(按任意字母退出): ";
    }
    cout << "Bye!\n";
    cin.clear();
    while (cin.get() != '\n')
    {
        continue;
    }
    
    return 0;
}

程序运行结果如下:
请输入目标距离(按任意字母退出): 50
输入步长:1
走了3553步后,当前位置:
(x, y) = (28.9761, -41.0075)
or
(m, a) = (50.2118, -54.7547)
平均每步向外距离 = 0.0141322
请输入目标距离(按任意字母退出): 50
输入步长:2
走了934步后,当前位置:
(x, y) = (10.1108, -49.3875)
or
(m, a) = (50.4119, -78.43)
平均每步向外距离 = 0.0539742
请输入目标距离(按任意字母退出): 50
输入步长:1
走了1262步后,当前位置:
(x, y) = (-33.2925, -38.2832)
or
(m, a) = (50.7345, -131.011)
平均每步向外距离 = 0.0402017
请输入目标距离(按任意字母退出): q
Bye!

原书中有这么一句话写得很好:
如果发现自己在随机漫步时,请保持自信,迈大步走。虽然在蜿蜒前进的过程中仍旧无法控制前进的方向,但至少会走得远一点。

加油!

posted @ 2024-04-06 21:05  superbmc  阅读(31)  评论(0)    收藏  举报