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;
}
}
那么接下来主要就是检查函数了,首先,本题的检查函数要能够将数据写入到文件,这需要使用头文件
首先,我们按照程序清单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;
}
于是程序运行结果如下图所示:
生成的文件内容如下图所示:
这是我们输入目标和步长两次的结果,虽然两次的目标和步长完全相同,但是模拟的毕竟只是仿真,而且都是随机的,可以看出两次的结果完全不同。
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;
}
运行结果如下图所示:
该结果与程序清单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;
}
运行结果如下图所示:
由上图可知,最高步数为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;
}
运行结果如下图所示:
该结果与程序清单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时就直接输出输入错误,程序结束。
运行结果如下图所示:
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;
}
运行结果如下图所示:
7. 复数有两个部分组成:实数部分和虚数部分。复数的一种书写方式是:(3.0,4.0),其中,3.0是实数部分,4.0是虚数部分。假设a=(A,Bi),c=(C,Di),则下面是一些复数运算。
请定义一个复数类,以便下面的程序可以使用它来获得正确的结果。
(……代码省略……)
注意。必须重载运算符<<和>>。标准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;
}
运行结果如下图所示: