【C++ Primer Plus】第11章 使用类 编程练习

1. 修改程序清单11.15,使之将一系列连续的随机漫步者位置写入到文件中。对于每个位置,用步号进行标示。另外,让该程序将初始条件(目标距离和步长)以及结果小结写入到该文件中。

 本题目与程序清单11.13,11.14和11.15相比较,主要改动为将在控制台输出改为了输出在文件中,由于程序本身已经重载了<<运算符,osteram是ofstream的基类,因此ofstream对象可以直接调用重载后的<<运算符。故vector.h和vector.cpp文件都不需要进行修改。

vector.h文件

#ifndef VECTOR_H
#define VECTOR_H
#include <iostream>
#include <fstream>

namespace VECTOR
{
    class Vector
    {
        public:
            enum Mode {RECT, POL};
        private:
            Mode mode;
            // RECT mode
            double x;
            double y;
            // POL mode
            double mag;
            double ang;
            // private methods for setting values
            void SetMag();
            void SetAng();
            void SetX();
            void SetY();
        public:
            Vector();
            Vector(double n1, double n2, Mode form = RECT);
            void Reset(double n1, double n2, Mode form = RECT);
            ~Vector() {};
            // report value
            double Xval() const {return x;}
            double Yval() const {return y;}
            double MagVal() const {return mag;}
            double AngVal() const {return ang;}
            // change mode
            void PolarMode();
            void RectMode();
            // operator overloading
            Vector operator+(const Vector & b) const;
            Vector operator-(const Vector & b) const;
            Vector operator-() const;
            Vector operator*(double n) const;
            // friend
            friend Vector operator*(double n, const Vector & b);
            friend std::ostream & operator<<(std::ostream & os, const Vector & v);
    };
}   // namespace VECTOR

#endif

vector.cpp文件

#include <cmath>
#include "vector.h"

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

namespace VECTOR
{
    const double Rad_to_Deg = 45.0 / atan(1.0);

    // private methods
    void Vector::SetMag()
    {
        mag = sqrt(x * x + y * y);
    }

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

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

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

    // public methods
    Vector::Vector()    // default constructor
    {
        x = y = mag = ang = 0.0;
        mode = RECT;
    }

    Vector::Vector(double n1, double n2, Mode form)
    {
        mode = form;
        if (mode == RECT)
        {
            x = n1;
            y = n2;
            SetMag();
            SetAng();
        }
        else if (mode == POL)
        {
            mag = n1;
            ang = n2 / Rad_to_Deg;
            SetX();
            SetY();
        }
        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 (mode == RECT)
        {
            x = n1;
            y = n2;
            SetMag();
            SetAng();
        }
        else if (mode == POL)
        {
            mag = n1;
            ang = n2 / Rad_to_Deg;
            SetX();
            SetY();
        }
        else
        {
            cout << "Incorrect 3rd argument to Vector() -- ";
            cout << "vector set to 0\n";
            x = y = mag = ang = 0.0;
            mode = RECT;
        }
    }

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

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

    // operator overloading
    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);
    }

    // friend methods
    Vector operator*(double n, const Vector & b)
    {
        return b * n;
    }

    // ostream << overloading
    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;
    }
} // namespace VECTOR

11.15 randwalk.cpp文件中,需要将ostream对象修改为ofstream即可。

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "vector.h"

int main()
{
    using namespace std;
    using VECTOR::Vector;
    srand(time(0));
    double direction;   // random direction
    Vector step;    // each strp
    Vector result(0.0, 0.0);    // result positon
    unsigned long steps = 0;    // total steps
    double target;  //target length
    double dstep;
    // write into file "save.txt"
    ofstream ofs;
    ofs.open("save.txt");
    // walk started
    cout << "Enter target distance (q to quit): ";
    while (cin >> target && target != 'q')
    {
        cout << "Enter step length: ";
        if (!(cin >> dstep))
            break;
        ofs << "Target Distance: " << target << ", " << "Step Size: " << dstep << std::endl;
        while (result.MagVal() < target)
        {
            ofs << steps << ": " << result << std::endl;
            direction = rand() % 360;
            step.Reset(dstep, direction, Vector::POL);
            result = result + step;            
            steps ++;
        }
        ofs << "After " << steps << "steps, the subject has the following location:\n";
        ofs << result << std::endl;
        ofs << "or" << std::endl;
        result.PolarMode();
        ofs << result << std::endl;
        ofs << "Average outward distance per step = " << target / steps << std::endl;

        steps = 0;
        result.Reset(0.0, 0.0);
        cout << "Enter target distance (q to quit): ";
    }
    cout << "Bye!\n";
    cin.clear();
    while (cin.get() != '\n')
        continue;

    system("pause");
    return 0;
}

程序输出保存在当前目录下的save.txt文件中

2. 对Vector类的头文件(程序清单 11.13)和实现文件(程序清单 11.14)进行修改,使其不再存储矢量的长度和角度,而是在magval()和angval()被调用时计算它们。

由于题目要求在vector.h头文件中不再存储矢量长度和角度,所以需要删除两个成员变量mag和ang,以及SetMag()和SetAng()两个函数。同时修改内联函数MagVal()和AngVal()使其不再返回成员变量的值,而是返回长度和角度的计算结果。

#ifndef VECTOR_H
#define VECTOR_H
#include <iostream>
#include <fstream>
#include <cmath>

namespace VECTOR
{
    class Vector
    {
        public:
            enum Mode {RECT, POL};
        private:
            Mode mode;
            // RECT mode
            double x;
            double y;
            // // POL mode
            // double mag;
            // double ang;
            // private methods for setting values
            // double SetMag();
            // double SetAng();
            void SetX(double mag, double ang);
            void SetY(double mag, double ang);
        public:
            Vector();
            Vector(double n1, double n2, Mode form = RECT);
            void Reset(double n1, double n2, Mode form = RECT);
            ~Vector() {};
            // report value
            double Xval() const {return x;}
            double Yval() const {return y;}
            double MagVal() const 
            {
                return sqrt(x * x + y * y);
            }
            double AngVal() const 
            {
                if (x == 0.0 && y == 0.0)
                    return 0.0;
                else
                    return atan2(y, x);
            }
            // change mode
            void PolarMode();
            void RectMode();
            // operator overloading
            Vector operator+(const Vector & b) const;
            Vector operator-(const Vector & b) const;
            Vector operator-() const;
            Vector operator*(double n) const;
            // friend
            friend Vector operator*(double n, const Vector & b);
            friend std::ostream & operator<<(std::ostream & os, const Vector & v);
    };
}   // namespace VECTOR

#endif

在vector.cpp文件中需要修改的是

  • 删除SetMag()以及SetAng()的成员函数定义
  • 修改构造函数和Reset()成员函数,如果传入RECT参数,则仅保存x,y坐标,如果传入POL参数,则将矢量长度和角度转换后的x,y坐标保存在成员变量中
#include "vector.h"

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

namespace VECTOR
{
    const double Rad_to_Deg = 45.0 / atan(1.0);

    // private methods
    // double Vector::SetMag()
    // {
    //     return MagVal();
    // }

    // double Vector::SetAng()
    // {
    //     return AngVal();
    // }

    void Vector::SetX(double mag, double ang)
    {
        x = mag * cos(ang);
    }

    void Vector::SetY(double mag, double ang)
    {
        y = mag * sin(ang);
    }

    // public methods
    Vector::Vector()    // default constructor
    {
        x = y = 0.0;
        mode = RECT;
    }

    Vector::Vector(double n1, double n2, Mode form)
    {
        mode = form;
        if (mode == RECT)
        {
            x = n1;
            y = n2;
            // SetMag();
            // SetAng();
        }
        else if (mode == POL)
        {
            double mag = n1;
            double ang = n2 / Rad_to_Deg;
            SetX(mag, ang);
            SetY(mag, ang);
        }
        else
        {
            cout << "Incorrect 3rd argument to Vector() -- ";
            cout << "vector set to 0\n";
            x = y = 0.0;
            mode = RECT;
        }
    }

    void Vector::Reset(double n1, double n2, Mode form)
    {
        mode = form;
        if (mode == RECT)
        {
            x = n1;
            y = n2;
            // SetMag();
            // SetAng();
        }
        else if (mode == POL)
        {
            double mag = n1;
            double ang = n2 / Rad_to_Deg;
            SetX(mag, ang);
            SetY(mag, ang);
        }
        else
        {
            cout << "Incorrect 3rd argument to Vector() -- ";
            cout << "vector set to 0\n";
            x = y = 0.0;
            mode = RECT;
        }
    }

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

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

    // operator overloading
    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);
    }

    // friend methods
    Vector operator*(double n, const Vector & b)
    {
        return b * n;
    }

    // ostream << overloading
    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.MagVal() << ", " << v.AngVal() * Rad_to_Deg << ")";
        }
        else
            os << "Vector object mode is invalid";
        return os;
    }
} // namespace VECTOR

程序清单11.15提供的randwalk.cpp文件不进行改动,用于测试程序结果

3. 修改程序清单11.15,使之报告N次测试中的最高、最低和平均数,而不是报告每次测试的结果。

本题目考察内容与类使用并无太大关联,只需要增加三个变量用于在循环中统计最高、最低和平均数即可。

4. 重新编写最后的Time类实例(程序清单11.10、程序清单11.11和程序清单11.12),使用友元函数来实现所有的重载运算符。

友元函数在类中进行声明,但是在源文件中定义的时候不需要在函数前添加类限定符::,但是需要在参数中添加类对象作为形参之一,表示该运算符作用的对象。

头文件mytime.h 

#ifndef MYTIME_H
#define MYTIME_H
#include <iostream>

class Time
{
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h, int m = 0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h = 0, int m = 0);
    friend Time operator+(const Time & t1, const Time & t2);
    friend Time operator-(const Time & t1, const Time & t2);
    friend Time operator*(double mult, const Time & t);
    friend Time operator*(const Time & t, double mult)
    {
        return mult * t;
    }
    friend std::ostream & operator<<(std::ostream & os, const Time & t);
};

#endif

在源文件mytime.cpp中队成员函数进行定义,针对小时和分钟运算使用到的思想,主要是将传入的时间对象统一转化为单位较小的分钟,针对折算成的总minutes值进行运算,将结果再转为标准的小时,分钟格式。

#include "mytime.h"

Time::Time()
{
    hours = minutes = 0;
};

Time::Time(int h, int m)
{
    hours = h;
    minutes = m;
}

void Time::AddMin(int m)
{
    minutes += m;
    hours += minutes / 60;
    minutes = minutes % 60;
}

void Time::AddHr(int h)
{
    hours += h;
}

void Time::Reset(int h, int m)
{
    hours = h;
    minutes = m;
}

Time operator+(const Time & t1, const Time & t2)
{
    Time result;
    result.minutes = t1.minutes + t2.minutes;
    result.hours = t1.hours + t2.hours + result.minutes / 60;
    result.minutes = result.minutes % 60;
    return result;
}

Time operator-(const Time & t1, const Time & t2)
{
    Time result;
    int tot1, tot2;
    tot1 = t1.minutes + 60 * t1.hours;
    tot2 = t2.minutes + 60 * t2.hours;
    result.minutes = (tot1 - tot2) % 60;
    result.hours = (tot1 - tot2) / 60;
    return result;
}

Time operator*(double mult, const Time & t)
{
    Time result;
    long totalMinutes = t.hours * 60 * mult + t.minutes * mult;
    result.minutes = totalMinutes % 60;
    result.hours = totalMinutes / 60;
    return result;
}

std::ostream & operator<<(std::ostream & os, const Time & t)
{
    os << t.hours << "hours, " << t.minutes << " minutes";
    return os;
}

用于测试程序的文件usetime.cpp相较于程序清单11.2没有改动,运行结果也保持一致

5.  重新编写Stonewt类(程序清单11.16和程序清单11.17),使它有一个状态成员,由该成员控制对象应转换为英石格式、整数磅格式还是浮点磅格式。重载<<运算符,使用它来替换show_stn()和show_lbs()方法。重载加法、减法和乘法运算符,以便可以对Stonewt值进行加、减、乘运算。编写一个使用所有类方法和友元的小程序,来测试这个类。

 头文件stonewt.h主要改动为

  • 使用枚举增加状态成员Mode,用于表示(英石+磅)和(磅)两种重量的表达方式
  • 成员函数ToPds()和ToStnPds()分别用于将对象转换为(磅)或者(英石+磅)两种格式
  • 使用友元函数对<<\+、-、*运算符进行重载
#ifndef STONEWT_H
#define STONEWT_H
#include <iostream>

class Stonewt
{
private:
    enum {Lbs_per_stn = 14};
    enum Mode {STN_PDS, PDS};
    int stone;
    double pdsLeft;
    double pounds;
    Mode mode;
public:
    Stonewt(double lbs);
    Stonewt(int stn, double lbs);
    Stonewt();
    ~Stonewt() {};
    void ToPds();
    void ToStnPds();
    friend std::ostream & operator<<(std::ostream & os, Stonewt & stn);
    friend Stonewt operator+(Stonewt & stn1, Stonewt & stn2);
    friend Stonewt operator-(Stonewt & stn1, Stonewt & stn2);
    Stonewt operator*(double n) const;
    friend Stonewt operator*(double n, Stonewt & stn)
    {
        return stn * n;
    }
};

#endif

在成员函数的定义文件stonewt.cpp中,针对+、-和*运算,用到了同样的计算方式,即将对象数值统一转换为单位较小的浮点数,对该浮点数计算得到的结果在进行转换,换为标准格式。

#include "stonewt.h"
using std::cout;

// default constructor
Stonewt::Stonewt()
{
    stone = pdsLeft = pounds = 0;
    mode = STN_PDS;
}

// construct Stonewt object from double value
Stonewt::Stonewt(double lbs)
{
    mode = PDS;
    stone = int (lbs) / Lbs_per_stn;
    pdsLeft = int (lbs) % Lbs_per_stn + lbs - int (lbs);
    pounds = lbs;
}

// construct Stonewt object from stone, double value
Stonewt::Stonewt(int stn, double lbs)
{
    mode = STN_PDS;
    stone = stn;
    pdsLeft = lbs;
    pounds = stn * Lbs_per_stn + lbs;
}

void Stonewt::ToPds()
{
    mode = PDS;
}

void Stonewt::ToStnPds()
{
    mode = STN_PDS;
}

std::ostream & operator<<(std::ostream & os, Stonewt & stn)
{
    if (stn.mode == Stonewt::STN_PDS)
    {
        os << stn.stone << " stone, " << stn.pdsLeft << " pounds";
    }
    else if (stn.mode == Stonewt::PDS)
    {
        os << stn.pounds << " pounds";
    }
    else
    {
        os << "Invalid object";
    }
    return os;
}

Stonewt operator+(Stonewt & stn1, Stonewt & stn2)
{
    Stonewt result;
    result.pounds = stn1.pounds + stn2.pounds;
    result.stone = int (result.pounds) / 14;
    result.pdsLeft = int (result.pounds) % 14 + result.pounds - int (result.pounds);
    return result;
}

Stonewt operator-(Stonewt & stn1, Stonewt & stn2)
{
    double pounds;
    pounds = stn1.pounds - stn2.pounds;
    Stonewt diff(pounds);
    return diff;
}

Stonewt Stonewt::operator*(double n) const
{
    return Stonewt(n * pounds);
}

在main.cpp测试该程序

#include <iostream>
#include "stonewt.h"
 
int main()
{
    using std::cout;
    using std::endl;
    Stonewt stn1(3.5);
    Stonewt stn2(12, 3.6);
    cout << "stn1: " << stn1 << endl;
    stn1.ToStnPds();
    cout << "stn1 after changing mode: " << stn1 << endl;
    cout << "stn2: " << stn2 << endl;
    stn2.ToPds();
    cout << "stn2 after changing mode: " << stn2 << endl;
    Stonewt sum = stn1 + stn2;
	cout << "stn1 + stn2 = " << sum << endl;
    Stonewt mult = 3 * stn1;
    cout << "3 * stn1 = " << mult << endl;

    system("pause");
    return 0;
}

运行结果

7. 复数有两个部分组成:实数部分和虚数部分。请定义一个复数类,以便下面的程序可使它来获得正确的结果

复数的定义和计算原理题目已经给出,所以我就直接贴代码了

conplex0.h

#ifndef COMPLEX0_H
#define COMPLEX0_H
#include <iostream>

class Complex
{
private:
    double real;
    double imaginary;
public:
    Complex();
    Complex(double r, double i);
    ~Complex() {};
    friend Complex operator+(const Complex & c1, const Complex & c2);
    friend Complex operator-(const Complex & c1, const Complex & c2);
    friend Complex operator*(const Complex & c1, const Complex & c2);
    friend Complex operator*(double x, const Complex & c);
    friend Complex operator~(const Complex & c);
    friend std::ostream & operator<<(std::ostream & os, const Complex & c);
    friend std::istream & operator>>(std::istream & os, Complex & c);
};

#endif

complex0.cpp

#include "complex0.h"

Complex::Complex()
{
    real = imaginary = 0.0;
}

Complex::Complex(double r, double i)
{
    real = r;
    imaginary = i;
}

Complex operator+(const Complex & c1, const Complex & c2)
{
    return Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}

Complex operator-(const Complex & c1, const Complex & c2)
{
    return Complex(c1.real - c2.real, c1.imaginary - c2.imaginary);
}

Complex operator*(const Complex & c1, const Complex & c2)
{
    return Complex(c1.real * c2.real - c1.imaginary * c2.imaginary, 
                   c1.real * c2.imaginary + c1.imaginary * c2.real);
}

Complex operator*(double x, const Complex & c)
{
    return Complex(x * c.real, x * c.imaginary);
}

Complex operator~(const Complex & c)
{
    return Complex(c.real, -c.imaginary);
}

std::ostream & operator<<(std::ostream & os, const Complex & c)
{
    os << "(" << c.real << ", " << c.imaginary << "i)";
    return os;
}

std::istream & operator>>(std::istream & is, Complex & c)
{
    using std::cout;
    cout << "real: ";
    is >> c.real;
    if (!is)
        return is;
    cout << "imaginart: ";
    is >> c.imaginary;
    return is;
}

使用题目给出的代码对程序进行测试,输出结果与预期一致

#include <iostream>
#include "complex0.h"
using namespace std;

int main()
{
    Complex a(3.0, 4.0);
    Complex c;
    cout << "Enter a complex number (q to quit):\n";
    while (cin >> c)
    {
        cout << "c is " << c << endl;
        cout << "complex conjugate is " << ~c << endl;
        cout << "a is " << a << endl;
        cout << "a + c is " << a + c << endl;
        cout << "a - c is " << a - c << endl;
        cout << "a * c is " << a * c << endl;
        cout << "2 * c is " << 2 * c << endl;
        cout << "Enter a complex number (q to quit)" << endl;
    }
    cout << "Done!\n";

    system("pause");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值