C++:矢量类(第十一章)

这一节主要是了解在同一个对象中包含两种描述同一个东西的不同模式,比如矢量有分直角坐标rectangle和极坐标系polar。体会一下类的优点。
本节设计一个程序:一个有关矢量的类,然后使用这个类做一个醉鬼实验(每一步就是一个矢量,走50米的随机实验)

设计类对象:

  1. 首先要有表示模式的变量,使用枚举(枚举和static是可以在类中使用表示为常量的)
  2. 基本对象数据:RECT里的x和y,POL里的长度和角度,可以在私有成员里加上设置x、y、长度和角度的成员函数。
  3. 公共成员函数有:构造函数、析构函数、showx、y、长度、角度的函数、转换为POL模式的函数、转换为RECT模式的函数、重载+ - * 和负号函数、<<运算符函数

类声明:

#ifndef VECT_H_
#define VECT_H_
#include<iostream>

namespace VECTOR{
class Vector
{
    public:
        enum Mode{RECT,POL};
    private:
        double x;
        double y;
        double length;
        double angle;
        Mode mode; 
        void set_x();
        void set_y();
        void set_l();
        void set_a();

    public:
        Vector();
        Vector(double n1, double n2, Mode form =RECT);
        ~Vector();
        double show_x() const {return x;}
        double show_y() const {return y;}
        double show_l() const {return length;}
        double show_a() const {return angle;}
        void show_mode() const {std::cout<<mode;}
        void pol_mode();
        void rect_mode();
        void reset(double n1,double n2,Mode form =RECT);
        Vector operator+(const Vector& v) const;
        Vector operator-(const Vector& v) const;
        Vector operator*(double n) const;
        Vector operator-() const;
        friend Vector operator*(double n,const Vector& v) ;
        friend std::ostream& operator<<(std::ostream & os, const Vector & v);
}; 
}
#endif

程序解析:

  1. 为了复习名字空间,该类放在namespace中
  2. 状态变量用enum表示(符号常量),且放在public中,因为主程序可能需要知道状态变量。
  3. 不会修改自身类对象的成员函数统一在末尾加上const,较短的函数在类声明中写出定义,将自动成为内联函数。
  4. private部分还有set_x,set_y,set_l和set_a函数,在两个不同坐标系下数据转换就不用计算了,直接调用函数。

源代码文件1

#include"vect.h"
#include<cmath>
using std::sqrt;
using std::sin;
using std::cos;
using std::atan;
using std::atan2;
using std::cout;

namespace VECTOR{
    const double Rag_to_deg =45.0/atan(1);
    void Vector::set_l(){
        length = sqrt(x*x+y*y);
    }
    void Vector::set_a(){
        if(x==0.0&&y==0.0)
            angle =0.0;
        else
            angle = atan2(y,x);
    }
    void Vector::set_x(){
        x=length *cos(angle);
    }
    void Vector::set_y(){
        y=length *sin(angle);
    }
    
    Vector::Vector(){
        x=y=length=angle=0;
        mode = RECT;
    }
    
    Vector::Vector(double n1,double n2,Mode form)
    {
        mode =form;
        if(form == RECT)
        {
            x=n1;
            y=n2;
            set_l();
            set_a();
        }
        else if(form == POL)
        {
            length =n1;
            angle =n2;
            set_x();
            set_y();
        }
        else
        {
            cout<<"There is no form like that,vector set to 0";
            x=y=angle=length =0;
            mode = RECT;
        }
    }
    
    void Vector::reset(double n1,double n2,Mode form)
    {   
        mode =form;
        if(form == RECT)
        {
            x=n1;
            y=n2;
            set_l();
            set_a();
        }
        else if(form == POL)
        {
            length =n1;
            angle =n2;
            set_x();
            set_y();
        }
        else
        {
            cout<<"There is no form like that,vector set to 0";
            x=y=angle=length =0;
            mode = RECT;
        }
    }
    
    void Vector::pol_mode(){
        mode = POL;
    }
    
    void Vector::rect_mode(){
        mode = RECT;
    }
    
    Vector::~Vector(){} 
    
    Vector Vector::operator+(const Vector& v) const{
        return Vector(x+v.x , y+v.y);
    }
    
    Vector Vector::operator*(double n) const{
        return Vector(x*n,y*n);
    }
    
    Vector Vector::operator-(const Vector & v) const
    {
        return Vector(x-v.x,y-v.y);
    }
    
    Vector Vector::operator-() const
    {
        return Vector(-x,-y);
    }
    
    Vector operator*(double n,const Vector & v)
    {
        return v*n;
    }
    
    std::ostream& operator<<(std::ostream& os, const Vector & v)
    {
        if(v.mode == Vector::RECT)
            os<<"(x,y) = ("<<v.x<<","<<v.y<<")\n";
        else if(v.mode == Vector::POL)
            os<<"(l,a) = ("<<v.length<<","<<v.angle* Rag_to_deg<<")\n";
        else
            os<<"Vector object mode is invalid\n";
        return os;
    }
}

程序解析:

  1. 用到了cmath库的数学公式。atan(y/x) 取值范围为[-π/2,π/2], atan2(-y,-x) 取值范围为[-π/π]
  2. 注意构造函数和reset函数要加上mode=form,不然可能发生意想不到的错误,后面会贴图。
  3. 运算符重载函数可以返回构造函数,这样的话将使用参数的值来创建一个无名对象然后返回该对象的副本。确保了对象是根据构造函数指定的标准规则创造的。
    而且简单了很多。如下面贴一个+重载运算符的计算方法
Vector operator+(const Vector & v) const{
      Vector  sum;
      sum.x = x + v.x;
      sum.y = y + v.y;
      set_a(); set_l();
      return sum; }

注意不要忘记要设计极坐标了,否则无法通过编译。

  1. 设计检测mode的值为RECT还是POL还是other有利于detect到难以发现的错误。

主程序

#include"vect.h"
#include<ctime>
#include<cstdlib>
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<<"Enter target distance:(q to quit): ";
    while(cin>>target)
    {
        cout<<"Enter step length: ";
        if(!(cin>>dstep))
            break;
        while(result.show_l()<target)
        {
            direction = rand()%360;
            step.reset(dstep,direction,Vector::POL);
            result =result + step;
            steps++;
        }
        cout<<"After "<<steps<<" steps, the object is in "<<result;
        cout<<" or ";
        result.pol_mode();
        cout<<result;
        cout<<"Average outward distance per step = "<<result.show_l()/steps<<endl;
        steps=0;
        result.reset(0.0,0.0);
        cout<<"Enter target distance:(q to quit): ";
    }
    cout<<"See yo"<<endl;
    cin.clear();
    while(cin.get()!='\n')  
        continue;
    return 0;
 } 

程序解析:

  1. 随机数,在cstdlib库文件中调用rand()和srand()函数。
    rand函数会返回一个0到某个值的随机整数(取决于实现),该程序取值为0-365(模拟醉鬼可能走的方向) rand()%360;但rand()函数通常会产生同样的随机数(将一种算法用作初始种子值得到随机数,该随机数作为下一次函数调用的种子)。srand()函数允许覆盖默认的种子值,使用time(0)的返回值设置种子,time(0)返回当前时间(0作为参数可以省略time_t变量声明,time()函数接受time_t变量地址,不做更多解释),所以srand(time(0))
  2. 当没有在构造函数或reset函数写下mode=form,result的mode值在做加法的时候(此时加法的返回值是构造函数Vector(..,..))将会变为-1.


    mode变为-1

    这样将无法打印直角坐标系(除非你在后面将result设置为rectangle模式,不过就显得很怪了)。
    后来debug了很久...


    image.png

    使用计算的方法来编写函数就对了,因为Vector sum是默认构造函数,它的mode就会自动设置为RECT。但Vector(.. , ..)被返回值调用的时候会创建一个无名新对象,然后这个无名新对象其实是没有初始化mode哒,为啥??所以这就解释了为什么result的mode是1.

    当添加上这一行代码,就finish了。

image.png

结果如上:假设要走50distance,每一步的长度为2,测试一共走了1061步,最后的位置在一个坐标上,每一步的偏差不超过0.047,可还行。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。