C++ Primer Plus(第六版)编程练习答案 第11章 使用类

本章所有编程练习的工程源码可在此处下载(点击此链接下载),供大家参考交流!

 

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

(……代码省略……)

本题要求将随机漫步者的位置写入到文件中,这些内容刚好和程序清单11.13,11.14,11.15的内容类似,所以我们可以效仿这几个例子来进行修改,以完成题目要求。(不知道为什么题目说的是修改程序清单11.5,猜测应该是“修改程序清单11.15”

首先我们把头文件拿过来,原封不动地就可以了,因为我们这里并没有什么新的功能,所有需要使用的功能程序清单11.13都包含了。

所以头文件vector.h代码如下:

// vector.h -- Vector class with <<, mode state
#ifndef VECTOR_H_
#define VECTOR_H_

#include 

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; } // repot values
        double yval() const { return y; }
        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);
    };
}

#endif

有了头文件之后,我们需要来为头文件里面的公有成员函数做函数定义,因为头文件和程序清单11.13完全相同,所以函数定义文件和程序清单11.14也就完全一样了,完全相同来定义就可以了。

所以vector.cpp代码如下:

//vector.cpp -- methods for the Vector class
//

#include "stdafx.h"
#include 
#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);

    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;
    }
}

那么接下来主要就是检查函数了,首先,本题的检查函数要能够将数据写入到文件,这需要使用头文件,并使用其中的fout函数来进行数据的写入;其实我们可以使用程序清单11.15的大框架,然后在此基础上进行功能的添加就可以了。

首先,我们按照程序清单11.15一模一样,根据随机种子来产生随机漫步者的随机方向和距离,初始化为原点,接下来加入写入到文件中的fout语句,

ofstream fout;
fout.open("RandWalk.txt");

下面这两句非常重要,第一句是定义fout为写入函数,fout.open就成为了打开文件的函数,因为此时目录中没有名字为“Randwalk.txt”的文件,所以就会自动去生成一个该名字的文件。

接下来和程序清单11.15相同,我们提示输入,但是一定要记得加上将输入结果写入到文件中的功能。对于题目要求的每一次显示结果都要在前面加上序号,我们必须自定义一个int整数类作为序号,每次将结果按照序号来写入。如下所示:

fout << i << ": (x,y) = (" << result.xval() << ", " << result.yval() << ")\n";
i++;

其中i就是序号。

其他的其实就没有什么特殊的了,功能上来说主要就是在程序11.13,11.14,11.15的基础上加了一个写入文件,以及按要求写入。

所以randwalk.cpp代码如下:

// randwalk.cpp -- using the Vector class
// compile with the vector.cpp file
#include "stdafx.h"
#include 
#include 
#include 
#include "vector.h"
#include 

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;
    ofstream fout;
    fout.open("RandWalk.txt");
    cout << "Enter target distance (q to quit): ";
    while (cin >> target)
    {
        cout << "Enetr step length: ";
        if (!(cin >> dstep))
        {
            break;
        }
        else
        {
            fout << "Target Distance: " << target << ", Step Size: " << dstep << endl;
        }
        int i = 0;
        while (result.magval() < target)
        {
            direction = rand() % 360;
            step.reset(dstep, direction, Vector::POL);
            result = result + step;
            steps++;
            fout << i << ": (x,y) = (" << result.xval() << ", " << result.yval() << ")\n";
            i++;
        }
        cout << "After " << steps << " steps, the subject has the following location:\n";
        cout << result << endl;
        fout << "After " << steps << " steps, the subject has the following location:\n";
        fout << result << endl;
        result.polar_mode();
        cout << " or\n" << result << endl;
        cout << "Average outward distance per step = " << result.magval() / steps << endl;
        fout << " or\n" << result << endl;
        fout << "Average outward distance per step = " << result.magval() / steps << endl;
        steps = 0;
        result.reset(0.0, 0.0);
        cout << "Enter target distance (q to quit): ";
    }
    cout << "Bye!\n";
    fout << "Bye!\n";
    cin.clear();
    while (cin.get() != '\n')
        continue;
	
    system("pause");
    return 0;
}

于是程序运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第1张图片

生成的文件内容如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第2张图片

这是我们输入目标和步长两次的结果,虽然两次的目标和步长完全相同,但是模拟的毕竟只是仿真,而且都是随机的,可以看出两次的结果完全不同。

 

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

应保留公有接口不变(公有方法及其参数不变),但对私有部分(包括一些私有方法)和方法实现进行修改。然后,使用程序清单11.15对修改后的版本进行测试,结果应该与以前相同,因为Vector类的公有接口与原来相同。

本题要求程序不再存储矢量的长度和角度,在调用magval()和angval()函数时再去计算长度和角度。题目要求通过修改私有部分来完成该功能,然后通过程序清单11.15来测试修改后的情况。

首先对于头文件,私有部分里面删去mag和ang这两个变量,然后在公有方法的函数定义里面,对于magval()和angval()函数,我们需要增加计算mag和ang的功能,其他内容完全相同。

所以头文件vector.h代码如下所示:

// vector.h -- Vector class with <<, mode state
#ifndef VECTOR_H_
#define VECTOR_H_
#include 
#include 

namespace VECTOR
{
    class Vector
    {
    public:
        enum Mode { RECT, POL };
    private:
        double x;
        double y;
        Mode mode;

        double set_mag();
        double set_ang();
        void set_x(double mag, double ang);
        void set_y(double mag, double ang);

    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; } // repot values
        double yval() const { return y; }
        double magval() const 
        {
            double mag;
            mag = sqrt(x * x + y * y);
            return mag; 
        }
        double angval() const 
        {
            double ang;
            if (x == 0.0 && y == 0.0)
                ang = 0.0;
            else
                ang = atan2(y, x);
            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);
    };
}

#endif

接下来对于实现文件,我们要特别注意,此时已经完全没有mag和ang这两个变量了,我们在实现各项功能时,要完全避免使用这两个变量,如果无法完全避免,就必须要调用magval()和angval()函数。

首先,对于set_mag()和set_ang()函数,我们必须在函数内部去定义mag和ang;然后是构造函数和reset函数,因为form等于POL时,要用到矢量的长度和角度;最后需要注意的是重载输出符号<<的函数,在该函数内部,我们需要去输出矢量的长度和角度,我们不能此时才去自定义mag和ang,而是必须使用Vector & v内部存在的,所以只能去调用magval()和angval()函数。

所以实现文件vector.cpp代码如下:

//vector.cpp -- methods for the Vector class
//

#include "stdafx.h"
#include 
#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);

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

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

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

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

    Vector::Vector()
    {
        x = y = 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)
        {
            double mag, ang;
            mag = n1;
            ang = n2 / Rad_to_deg;
            set_x(mag, ang);
            set_y(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 (form == RECT)
        {
            x = n1;
            y = n2;
            set_mag();
            set_ang();
        }
        else if (form == POL)
        {
            double mag, ang;
            mag = n1;
            ang = n2 / Rad_to_deg;
            set_x(mag, ang);
            set_y(mag, ang);
        }
        else
        {
            cout << "Incorrect 3rd argument to Vector() -- ";
            cout << "vector set to 0\n";
            x = y = 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.magval() << ", " << v.angval() * Rad_to_deg << ")";
        }
        else
            os << "Vector object mode is invalid";
        return os;
    }
}

于是接下来,我们再把程序清单11.15复制过来就可以了,randwalk.cpp代码如下:

// randwalk.cpp -- using the Vector class
// compile with the vector.cpp file
#include "stdafx.h"
#include 
#include 
#include 
#include "vector.h"

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 << "Enetr step length: ";
        if (!(cin >> dstep))
        {
            break;
        }
        while (result.magval() < target)
        {
            direction = rand() % 360;
            step.reset(dstep, direction, Vector::POL);
            result = result + step;
            steps++;
        }
        cout << "After " << steps << " steps, the subject has the following location:\n";
        cout << result << endl;
        result.polar_mode();
        cout << " or\n" << result << endl;
        cout << "Average outward distance per step = " << result.magval() / steps << 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;
}

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第3张图片

该结果与程序清单11.15与程序清单11.13和11.14一起运行时的结果完全相同,只是因为随机性所以具体数据不同而已。

 

3. 修改程序清单11.15,使之报告N次测试中的最高、最低和平均步数(其中N是用户输入的整数),而不是报告每次测试的结果。

本题明确要求你修改程序清单11.15,所以头文件和实现文件你就直接使用程序清单11.13和11。14就可以了,这里就不贴了,前面几题已经做过类似的工作了,ctrl A+C+V就可以了。

对于randwalk.cpp,首先,我们要定义一个表示测试次数的变量N,然后定义表示最高、最低和总步数的int类型变量为maxsteps,minsteps,sumsteps,分别初始化为0,65535,0,因为我们是把每一次的结果和已有的maxsteps和minsteps进行比较,如果大于maxsteps就更新maxsteps,小于minsteps就更新minsteps,所以它们的初值一定要设为int类型的最小值和最大值才最方便;而对于总步数的sumsteps,当然是从0开始走出来的。接下来再定义一个avesteps,double类型,因为将sumsteps除以N之后有可能是小数。

在这里必须明确一共测试多少次,所以在提示用户输入目标和步长之前,应提示用户输入测试次数。

在这里,对于每一次随机的漫步,我们都需要把内部获得的steps数据同maxsteps和minsteps进行比较和更新,然后把steps累加进sumsteps里。当N次测试循环结束之后,我们再来计算avesteps,并将结果进行输出。

在这里由于测试次数一定,我们不再需要原来程序清单11.15里的最外边的while循环,因为我们只需要一个target和一个dstep,只是在此基础上去循环测试,所以我们此时的循环应该是for循环,i从0到N。

如上所述,randwalk.cpp代码如下所示:

// randwalk.cpp -- using the Vector class
// compile with the vector.cpp file
#include "stdafx.h"
#include 
#include 
#include 
#include "vector.h"

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;
    int N;
    int maxsteps, minsteps, sumsteps;
    maxsteps = 0;
    minsteps = 65535;
    sumsteps = 0;
    double avesteps;
    cout << "How many times of test would you want: ";
    cin >> N;
    cout << "\nEnter target distance: ";
    cin >> target;
    cout << "Enetr step length: ";
    cin >> dstep;
    cout << endl;
    for (int i = 0; i < N; i++)
    {
        while (result.magval() < target)
        {
            direction = rand() % 360;
            step.reset(dstep, direction, Vector::POL);
            result = result + step;
            steps++;
        }
        sumsteps += steps;
        if (steps > maxsteps)
        {
            maxsteps = steps;
        }
        if (steps < minsteps)
        {
            minsteps = steps;
        }
        steps = 0;
        result.reset(0.0, 0.0);
    }
    avesteps = sumsteps / N;
    cout << "Walk finished!\nAfter " << N << " times of test, the result is following:\n";
    cout << "The maxmum steps is " << maxsteps << ", the minimum steps is " << minsteps << ", and the average steps is " << avesteps << ".\n";	
    cout << "Bye!\n";
    cin.clear();
    while (cin.get() != '\n')
        continue;
	
    system("pause");
    return 0;
}

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第4张图片

由上图可知,最高步数为1967,最低步数为129,平均步数为575,比较符合实际情况。

 

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

本题要求将Time类示例的重载运算符都使用友元函数来实现。

现在的程序清单11.10里面,对于加法减法和乘法,是直接使用公有方法来重载的,使用公有方法还是友元一般根据实际情况而定,这里既然题目这样要求了,我们就只能这样做。

其实将公有方法改成友元函数非常简单,首先在头文件的公有方法前面加上friend,其次在实现文件中定义函数时,去掉Time::作用域,因为此时友元函数并不属于该类,所以就当做普通函数来定义即可。

首先,头文件mytime3.h代码如下所示:

// mytime3.h -- Time class with friends
#ifndef MYTIME3_H_
#define MYTIME3_H_
#include 

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*(const Time & t, double n);
    friend Time operator*(double m, const Time & t);
    friend std::ostream & operator<<(std::ostream & os, const Time & t);
};

#endif

这里需要注意的是看起来*运算符重载了两次,但其实这两次不一样,第一次是重载了t*n的情况,调用的是Time类对象,乘的是double类型的变量,而第二次恰恰相反,调用的是double类型变量,乘的是Time类对象。

而对于实现文件,其实主要就是去掉函数的作用域,其他不怎么需要修改。

所以mytime3.cpp代码如下所示:

// mytime3.cpp -- implementing Time methods
#include "stdafx.h"
#include "mytime3.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 %= 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 sum;
    sum.minutes = t1.minutes + t2.minutes;
    sum.hours = t1.hours + t2.hours + sum.minutes / 60;
    sum.minutes %= 60;
    return sum;
}

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

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

Time operator*(double mult, const Time & t)
{
    Time result;
    long totalminutes = t.hours * mult * 60 + 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;
}

而接下来对于检查函数,使用程序清单11.12即可。

所以usetime3.cpp代码如下所示:

// usetime3.cpp -- using fourth draft of the Time class
// compile usetime3.cpp and mytime3.cpp together
#include "stdafx.h"
#include 
#include "mytime3.h"

int main()
{
    using std::cout;
    using std::endl;
    Time aida(3, 35);
    Time tosca(2, 48);
    Time temp;

    cout << "Aida and Tosca:\n";
    cout << aida << "; " << tosca << endl;
    temp = aida + tosca;
    cout << "Aida + Tosca: " << temp << endl;
    temp = aida * 1.17;
    cout << "Aida * 1.17: " << temp << endl;
    cout << "10.0 * Tosca: " << 10.0 * tosca << endl;

    system("pause");
    return 0;
}

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第5张图片

该结果与程序清单11.12和程序清单11.10和11.11一起运行得出的结果完全相同。

 

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

本题首先要求定义一个状态成员作为标志位,控制对象转换成何种格式,所以我们在私有部分添加一个int类型变量即可,分别用不同的整数代表不同的格式。另外在公有方法部分,再加一个设置状态的函数,用来给对象设置状态,输入参数即是标识不同状态的int变量。

接下来是重载各种运算符。首先,题目明确要求重载<<运算符,用来代替两个show函数;另外还需要重载加、减、乘运算符,用来进行各种运算。对于加法和减法,基本上都是对象之间进行才有意义,而对于乘法,则是对象和double类型浮点数之间进行才有意义,所以这里就涉及是Stonewt*double还是double*Stonewt。所以一共需要5个重载运算符函数,我们统一使用友元函数来完成任务。

所以头文件stonewt.h代码如下所示:

// stonewt.h -- definition for the Stonewt class
#ifndef STONEWT_H_
#define STONEWT_H_
#include 

class Stonewt
{
private:
    enum { Lbs_per_stn = 14 };
    int state; 
    int stone;
    double pds_left;
    double pounds;
public:
    Stonewt(double lbs);
    Stonewt(int stn, double lbs);
    Stonewt();
    ~Stonewt();
    void Stonewt::setstate(int x);
    friend Stonewt operator+(const Stonewt & s1, const Stonewt & s2);
    friend Stonewt operator-(const Stonewt & s1, const Stonewt & s2);
    friend Stonewt operator*(const Stonewt & s, double n);
    friend Stonewt operator*(double m, const Stonewt & s);
    friend std::ostream & operator<<(std::ostream & os, const Stonewt & s);
};

#endif

有了头文件之后,我们来看看实现文件。

首先对于设置状态的函数,我们只需要将输入参数赋给标识状态的int变量即可。

接下来对于5个重载运算符的友元函数,对于加法和减法,我们都是应该首先对pounds变量进行计算,接下来就有两个思路,一个是有了新计算出来的pounds值之后,根据计算出来的pounds值和Lbs_per_stn值来进行计算,获得stone值和pds_left值,另一种思路是直接把计算出来的pounds值作为参数赋给一个新的构造函数,使用构造函数来构造一个新的对象,这样新的对象的各种项数值都可以根据输入参数pounds值获得。

对于乘法运算符,无论是Stonewt*double还是double*Stonewt,其实内部计算都完全相同,都是把对象的pounds值去乘参数x,然后把计算出来的pounds值作为参数赋给构造函数去新建立一个对象,获得其他值。

对于输出的<<运算符的重载会比较复杂,因为使用不同的格式输出,显示的情况会不同。所以此时,就需要明确不同的int类型变量代表不同的格式,在这里我是定义整数1代表“stone+pounds”格式,整数2代表整数“pounds”格式,整数3是代表浮点数“pounds”格式。

所以实现文件stonewt.cpp代码如下所示:

// stonewt.cpp -- Stonewt methods
#include "stdafx.h"
#include 
#include "stonewt.h"

using std::cout;

Stonewt::Stonewt(double lbs)
{
    stone = int(lbs) / Lbs_per_stn;
    pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
    pounds = lbs;
}

Stonewt::Stonewt(int stn, double lbs)
{
    stone = stn;
    pds_left = lbs;
    pounds = stn * Lbs_per_stn + lbs;
}

Stonewt::Stonewt()
{
    stone = pounds = pds_left = 0;
    state = 3;
}

Stonewt::~Stonewt()
{
}

void Stonewt::setstate(int x)
{
    state = x;
}

Stonewt operator+(const Stonewt & s1, const Stonewt & s2)
{
    Stonewt sum;
    sum.pounds = s1.pounds + s2.pounds;
    sum.stone = int(sum.pounds) / 14;
    sum.pds_left = int(sum.pounds) % 14 + sum.pounds - int(sum.pounds);
    return sum;
}

Stonewt operator-(const Stonewt & s1, const Stonewt & s2)
{
    double pounds;
    pounds = s1.pounds - s2.pounds;
    Stonewt diff(pounds);
    return diff;

}

Stonewt operator*(const Stonewt & s, double n)
{
    double pounds;
    pounds = s.pounds * n;
    Stonewt mult(pounds);
    return mult;
}

Stonewt operator*(double m, const Stonewt & s)
{
    double pounds;
    pounds = s.pounds * m;
    Stonewt mult(pounds);
    return mult;
}


std::ostream & operator<<(std::ostream & os, const Stonewt & s)
{
    if (s.state == 1)
    {
        os << s.stone << " stone, " << s.pds_left << " pounds\n";
    }
    if (s.state == 2)
    {
        os << int(s.pounds) << " pounds\n";
    }
    if (s.state == 3)
    {
        os << s.pounds << " pounds\n";
    }
    return os;
}

有了头文件和实现文件之后,就要来看看我们需要实现哪些功能,根据功能来编写检查文件测试这些功能。

首先,我们需要明确提示用户希望使用哪种方式来显示结果,然后根据用户输入的整数来区分;在此注意,因为之前是规定了整数1,2,3分别表示的意义,所以这里需要提示用户待选的输入值,以及各个待选输入值表示的意义,供用户知晓后选择。

接下来就是检测各个运算符了,有加法,减法,两种乘法,分别用四个不同的对象来实现即可。

检测文件stone.cpp代码如下所示:

// stone.cpp -- user-defined conversions
// compile with stonewt.cpp

#include "stdafx.h"
#include 
#include "stonewt.h"
using namespace std;

int main()
{
    Stonewt s[3];
    Stonewt s1(275);
    Stonewt s2(285.7);
    Stonewt s3(21, 8);
    s[0] = s1;
    s[1] = s2;
    s[2] = s3;
    int state;
    for (int i = 0; i < 3; i++)
    {
        cout << "#" << i + 1 << ": \n";
        cout << "Choose your style to display( '1' for stones, '2' for integer pounds, '3' for float pounds, others to quit): ";
        cin >> state;
        if (state != 1 && state != 2 && state != 3)
        {
            cout << "Warning: style can't satisfied!" << endl;
            cout << "Bye!\n";
            system("pause");
            return 0;
        }
        s[i].setstate(state);
        cout << "s" << i + 1 << ": " << s[i] << endl;
    }

    int st;
    cout << "Choose your style to display the result( '1' for stones, '2' for integer pounds, '3' for float pounds, others to quit): ";
    cin >> st;
    Stonewt sum;
    sum = s1 + s2;
    sum.setstate(st);
    cout << "s1 + s2 = " << sum << endl;
    Stonewt diff;
    diff = s3 - s2;
    diff.setstate(st);
    cout << "s3 - s2 = " << diff << endl;
    Stonewt mult1, mult2;
    mult1 = 10 * s1;
    mult2 = s3 * 1.5;
    mult1.setstate(st);
    mult2.setstate(st);
    cout << "10 * s1 = " << mult1 << endl;
    cout << "s3 * 1.5 = " << mult2 << endl;
	
    system("pause");
    return 0;
}

这里我是对用户的输入做了明确的说明,即1,2,3分别表示什么意思,以及当用户输入的不是1,2,3时就直接输出输入错误,程序结束。

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第6张图片

 

6. 重新编写Stonewt类(程序清单11.16和程序清单11.17),重载全部6个关系运算符。运算符对pounds成员进行比较,并返回一个bool值。编写一个程序,它声明一个包含6个Stonewt对象的数组,并在数组声明初始化前3个对象。然后使用循环来读取用于设置剩余3个数组元素的值。接着报告最小的元素、最大的元素以及大于或等于11英石的元素的数量(最简单的方法是创建一个Stonewt对象,并将其初始化为11英石,然后将其同其他对象进行比较)。

首先本题要求重载全部6个关系运算符,即<,>,==,<=,>=,!=这六种。比较之后返回一个bool值,即是表示是否满足该运算符的关系,是就是返回true,否则返回false。据此就可以写出头文件和实现文件了。由于这些关系运算符都是需要对两个Stonewt对象进行比较,所以我们都定义成友元函数比较方便。

头文件stonewt.h代码如下所示:

// stonewt.h -- definition for the Stonewt class
#ifndef STONEWT_H_
#define STONEWT_H_
#include 

class Stonewt
{
private:
    enum { Lbs_per_stn = 14 };
    int state;
    int stone;
    double pds_left;
    double pounds;
public:
    Stonewt(double lbs);
    Stonewt(int stn, double lbs);
    Stonewt();
    ~Stonewt(); 
    void Stonewt::setstate(int x);
    friend bool operator<(const Stonewt & s1, const Stonewt & s2);
    friend bool operator>(const Stonewt & s1, const Stonewt & s2);
    friend bool operator==(const Stonewt & s1, const Stonewt & s2);
    friend bool operator<=(const Stonewt & s1, const Stonewt & s2);
    friend bool operator>=(const Stonewt & s1, const Stonewt & s2);
    friend bool operator!=(const Stonewt & s1, const Stonewt & s2);
    friend std::ostream & operator<<(std::ostream & os, const Stonewt & s);
};

#endif

根据头文件定义的公有方法,我们只需要对几大关系运算符函数进行比较即可。

实现文件stonewt.cpp代码如下所示:

// stonewt.cpp -- Stonewt methods
#include "stdafx.h"
#include 
#include "stonewt.h"

using std::cout;

Stonewt::Stonewt(double lbs)
{
    stone = int(lbs) / Lbs_per_stn;
    pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
    pounds = lbs;
    state = 1;
}

Stonewt::Stonewt(int stn, double lbs)
{
    stone = stn;
    pds_left = lbs;
    pounds = stn * Lbs_per_stn + lbs;
    state = 1;
}

Stonewt::Stonewt()
{
    stone = pounds = pds_left = 0;
    state = 1;
}

Stonewt::~Stonewt()
{
}

void Stonewt::setstate(int x)
{
    state = x;
}

bool operator<(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds < s2.pounds)
        return true;
    else
        return false;
}

bool operator>(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds > s2.pounds)
        return true;
    else
        return false;
}

bool operator==(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds == s2.pounds)
        return true;
    else
        return false;
}

bool operator<=(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds <= s2.pounds)
        return true;
    else
        return false;
}

bool operator>=(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds >= s2.pounds)
        return true;
    else
        return false;
}

bool operator!=(const Stonewt & s1, const Stonewt & s2)
{
    if (s1.pounds != s2.pounds)
        return true;
    else
        return false;
}

std::ostream & operator<<(std::ostream & os, const Stonewt & s)
{
    if (s.state == 1)
    {
        os << s.stone << " stone, " << s.pds_left << " pounds";
    }
    if (s.state == 2)
    {
        os << int(s.pounds) << " pounds";
    }
    if (s.state == 3)
    {
        os << s.pounds << " pounds";
    }
    return os;
}

在这里我是在上面第5题的基础上进行修改来做第6题的,所以我依然保留了那个state变量,并且也保留了setstate的公有方法来设置状态。

这里定义6个关系运算符时,其实都非常简单,就是把输入的两个对象的pounds值拿去比较一下就可以了,if满足就返回true,else就返回false。

接下来对于检测文件,题中要求声明一个包含6个Stonewt对象的数组,前3个对象在声明中进行初始化,然后使用循环来读取后3个数组元素的值。这里就需要提示用户输入的模式了,我觉得直接输入pounds值比较方便,所以后3个的输入方式我定义的是直接输入pounds值。

输入结束之后,程序需要去互相比较,从而获得最大元素、最小元素和大于等于11英石的元素。

在这里做比较时,我是再去定义三个Stonewt对象,分别是max,min和flag,max初始化为0,min初始化为65535,而falg初始化为11,然后将之前已经确定了数值的6个Stonewt对象与这三个对象分别进行比较,来更新max和min,以及记录大于11英石的对象。题目只要求我们得出大于等于11英石的元素的数量,并不需要知道是哪几个满足条件,所以声明一个int变量来存储数量即可。

所以检测文件stone.cpp代码如下所示:

// stone.cpp -- user-defined conversions
// compile with stonewt.cpp

#include "stdafx.h"
#include 
#include "stonewt.h"

using namespace std;

int main()
{
    Stonewt s[6] = { {275}, {288.22}, {26, 7} };
    for (int i = 3; i < 6; i++)
    {
        double pounds;
        cout << "Please enter the last 3 Stonewt class members(enter the entire pounds): \n";
        cout << "#" << i+1 << ": ";
        cin >> pounds;
        s[i] = pounds;
	}
    cout << "Enter finished!\n\n";
    cout << "So the whole 6 Stonewt class members are: \n";
    cout << "#1: " << s[0] << endl;
    cout << "#2: " << s[1] << endl;
    cout << "#3: " << s[2] << endl;
    cout << "#4: " << s[3] << endl;
    cout << "#5: " << s[4] << endl;
    cout << "#6: " << s[5] << endl;

    Stonewt mins = 65535;
    Stonewt maxs = 0;
    int num = 0;
    Stonewt flag(11,0);
    for (int i = 0; i < 6; i++)
    {
        if (s[i] < mins)
            mins = s[i];
        if (s[i] > maxs)
            maxs = s[i];
        if (s[i] >= flag)
            num++;
    }

    cout << "After comparation:\n";
    cout << "The maximum Stonewt class member is " << maxs << endl;
    cout << "The minimum Stonewt class member is " << mins << endl;
    cout << "There are " << num << " Stonewt class members bigger than 11 stones" << endl;
    cout << endl;
    cout << "Comparation finished!\nBye!\n";

    system("pause");
    return 0;
}

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第7张图片

 

7. 复数有两个部分组成:实数部分和虚数部分。复数的一种书写方式是:(3.0,4.0),其中,3.0是实数部分,4.0是虚数部分。假设a=(A,Bi),c=(C,Di),则下面是一些复数运算。

  • 加法:a+c=(A+C,(B+D)i)。
  • 减法:a-c=(A-C,(B-D)i)。
  • 乘法:a*c=(A*C-B*D,(A*D+B*C)i)。
  • 乘法:x*c=(x*C,x*Di),其中x为实数。
  • 共轭:~a=(A,-Bi)。

请定义一个复数类,以便下面的程序可以使用它来获得正确的结果。

(……代码省略……)

注意。必须重载运算符<<和>>。标准C++使用头文件complex提供了比这个示例更广泛的复数支持,因此应将自定义的头文件命名为complex0.h,以免发生冲突。应尽可能使用const。

下面是该程序的运行情况。

(……代码省略……)

请注意,经过重载后,cin>>c将提示用户输入实数和虚数部分。

首先,本题要求能够处理复数的实数和虚数部分,所以头文件的私有部分,一定要分别定义实数和虚数部分,都是double类型。

其次,对于公有方法,首先一定要提供构造函数,除了默认构造函数之外,还应该定义一个可以初始化的构造函数,即输入参数为两个double类型变量,分别表示实数部分和虚数部分。

接下来就是运算符了,对于题目要求的5种运算,需要声明5个不同的友元函数,然后我们还需要输入输出两个友元函数。

所以头文件complex0.h代码如下所示:

#ifndef COMPLEX0_H_
#define COMPLEX0_H_
#include 

class complex
{
private:
    double real;//实数部分
    double ima;//虚数部分
public:
    complex();
    complex(double x, double y);
    ~complex();
    friend complex operator+(const complex & c1, const complex & c2);
    friend complex operator-(const complex & c1, const complex & c2);
    friend complex operator*(double n, const complex & c);
    friend complex operator*(const complex & c1, const complex & c2);
    friend complex operator~(const complex & c);
    friend std::ostream & operator<<(std::ostream & os, const complex & c);
    friend std::istream & operator>>(std::istream & is, complex & c);
};

#endif

有了头文件之后,我们来写实现文件。

首先对于默认构造函数,我直接定义为(0,0),即实数和虚数部分均为0.

接下来主要就是5种运算符的友元函数的定义。首先对于加法,我们需要再声明一个complex对象,然后把输入的两个complex对象的实数部分对应相加,虚数部分对应相加,把两个结果分别存储到新创建的complex对象的实数和虚数部分,再返回新创建的complex对象即可。减法类似。

而对于乘法,根据题中的公式,如果是两个complex对象相乘,则与加法减法类似,只是计算公式比较复杂而已;而如果是浮点数与complex对象相乘,也只需要按照公式来即可。思路都一样,都是先创建一个新的complex对象,用新的complex对象来存储计算结果。

对于共轭,我们沿用相同的思路,创建新的complex对象,实数直接赋值,虚数取负之后赋值,返回新的complex对象。

对于输入输出的运算符重载,输出的情况已经写过很多次了,非常简单;而输入相对来说会比较复杂,因为这里输入的时候要区分实数部分和虚数部分,所以索性提示用户分开输入就可以了。

实现文件sourcefunc.cpp代码如下所示:

// sourcefunc.cpp -- The functions' definition

#include "stdafx.h"
#include 
#include "complex0.h"
using namespace std;

complex::complex()
{
    real = ima = 0.0;
}

complex::complex(double x, double y)
{
    real = x;
    ima = y;
}

complex::~complex()
{
}

complex operator+(const complex & c1, const complex & c2)
{
    complex sum;
    sum.real = c1.real + c2.real;
    sum.ima = c1.ima + c2.ima;
    return sum;
}

complex operator-(const complex & c1, const complex & c2)
{
    complex diff;
    diff.real = c1.real - c2.real;
    diff.ima = c1.ima - c2.ima;
    return diff;
}

complex operator*(const complex & c1, const complex & c2)
{
    complex mult;
    mult.real = c1.real * c2.real - c1.ima * c2.ima;
    mult.ima = c1.real * c2.ima + c1.ima * c2.real;
    return mult;
}

complex operator*(double n, const complex & c)
{
    complex mult;
    mult.real = n * c.real;
    mult.ima = n * c.ima;
    return mult;
}

complex operator~(const complex & c)
{
    complex conj;
    conj.real = c.real;
    conj.ima = -c.ima;
    return conj;
}

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

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

有了头文件和实现文件之后,我们来写检测文件检测功能。

其实本题检测功能非常简单,就是5种计算,然后能够直接输入和输出对象即可。题中已给出检测文件的代码,抄上去即可。

检测文件complex_calculate.cpp代码如下所示:

// complex_calculate.cpp -- check the functions

#include "stdafx.h"
#include 
using namespace std;
#include "complex0.h"

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 << "\n";
        cout << "complex conjugate is " << ~c << "\n";
        cout << "a is " << a << "\n";
        cout << "a + c is " << a + c << "\n";
        cout << "a - c is " << a - c << "\n";
        cout << "a * c is " << a * c << "\n";
        cout << "2 * c is " << 2 * c << "\n";
        cout << "Enter a complex number (q to quit):\n";
    };
    cout << "Done!\n";

    system("pause");
    return 0;
}

运行结果如下图所示:

C++ Primer Plus(第六版)编程练习答案 第11章 使用类_第8张图片

 

本章所有编程练习的工程源码可在此处下载(点击此链接下载),供大家参考交流!

你可能感兴趣的:(C++,C++,Primer,Plus,(第六版),中文版,编程练习)