前言:
img
软件学院《C++Ⅱ》试卷
======================== ##
一、给出一个有理数类,实现有理数的加法运算。请按后列要求作答。
1.仔细、完整地阅读程序,对应题号填写注释。注释的内容包括两部分:1.本行的语句是
什么;2.简述其功能或特点。例如,注释可以这样写:“友员函数;重载加法运算符(+)。”
(每小题 1 分,共 10 分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<iostream.h>
#include<math.h>
#include<iomanip.h>
class Rational
{ private:
int numerator,denominator; int maxcommonFactor(int,int);
void simplify( );
void DtoF(double,int&,int&);
public: Rational(int n,int d=1);
Rational(double x=0);
~Rational();
void plus(const Rational&);
Rational operator + (const Rational&);
void showFraction( );
operator double( );
friend ostream &operator<<
(ostream &output, Rational &t);
};
|
私有成员函数; 求两个整数的最大公因子。 (2) 私有成员函数; 分数约简。
(3) 私有成员函数; 小数转换为分数。 (4) 构造函数; 参数为分子、
分母形式。 (5) 重载构造函数; 参数为小数形式。 (6) 析构函数;
对象的释放清理。 (7) 公有成员函数; 实现加法运算。 (8) 公有成员函数;
以分数形式输出有理数。 (9) 类型转换函数; 把分数形式的对象转换成小数。
(10) 友员函数; 重载插入运算符 (<<) 。 ****
2.
对应题号把函数的定义补充完整。(每小题 3 分,共 9 分)
正确为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| int Rational::maxcommonFactor(int a, int b) { if (a % b == 0) return b; return maxcommonFactor(b, a % b); }
void Rational::simplify() { int temp = maxcommonFactor(abs(numerator), abs(denominator)); if (temp == 1) return; numerator /= temp; denominator /= temp; }
void Rational::DtoF(double x, int &n, int &d) { double s = fabs(x); d = 1; while ((s * d - int(s * d)) > 1e-15) d *= 10; n = int(x * d); }
Rational::Rational(int n, int d) { if (d == 0) { cout << "错误! 分母不能为 0。有理数将置为 0。" << endl; numerator = 0; denominator = 1; } else { numerator = n; denominator = d; } simplify(); }
Rational::Rational(double x) { DtoF(x, numerator, denominator); simplify(); }
Rational Rational::operator+(const Rational &t) { Rational temp(0, 1); temp.numerator = numerator * t.denominator + denominator * t.numerator; temp.denominator = denominator * t.denominator; temp.simplify(); return temp; }
Rational::operator double() { return static_cast<double>(numerator) / denominator; }
ostream &operator<<(ostream &output, Rational &t) { output << t.numerator << '/' << t.denominator; return output; }
void main() { Rational a; cout << double(a); cout << endl;
Rational b(2, 0); b.showFraction(); cout << endl;
Rational c(3, 4); c.showFraction(); cout << endl;
Rational d(1.2); d.showFraction(); cout << endl;
a = b + c; cout << double(a); cout << endl; }
|
这段代码定义了一个表示有理数的类
Rational
,并实现了几个功能,如最大公约数计算、简化分数、从双精度浮点数转换为分数、加法运算符重载、类型转换运算符重载以及输出运算符重载。下面是对每个部分的详细解释:
类定义及成员函数实现
最大公约数计算函数
1 2 3 4 5
| int Rational::maxcommonFactor(int a, int b) { if (a % b == 0) return b; return maxcommonFactor(b, a % b); }
|
- 使用递归方法计算两个整数
a
和 b
的最大公约数。基于欧几里得算法,如果 a
能被 b
整除,则 b
是最大公约数,否则递归调用自己。
简化分数函数
1 2 3 4 5 6 7
| void Rational::simplify() { int temp = maxcommonFactor(abs(numerator), abs(denominator)); if (temp == 1) return; numerator /= temp; denominator /= temp; }
|
- 使用
maxcommonFactor
计算分子和分母的最大公约数,然后用该值除以分子和分母以简化分数。
双精度浮点数转换为分数函数
1 2 3 4 5 6 7
| void Rational::DtoF(double x, int &n, int &d) { double s = fabs(x); d = 1; while ((s * d - int(s * d)) > 1e-15) d *= 10; n = int(x * d); }
|
- 将双精度浮点数
x
转换为分子 n
和分母
d
的形式。通过不断增加 d
,直到
s * d
变为整数。
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Rational::Rational(int n, int d) { if (d == 0) { cout << "错误! 分母不能为 0。有理数将置为 0。" << endl; numerator = 0; denominator = 1; } else { numerator = n; denominator = d; } simplify(); }
Rational::Rational(double x) { DtoF(x, numerator, denominator); simplify(); }
|
- 构造函数
Rational(int n, int d)
:根据给定的分子
n
和分母 d
初始化 Rational
对象。如果分母为 0
,则输出错误信息并将对象设置为
0/1
。然后调用 simplify
简化分数。
- 构造函数
Rational(double x)
:将双精度浮点数
x
转换为分数形式并简化。
运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Rational Rational::operator+(const Rational &t) { Rational temp(0, 1); temp.numerator = numerator * t.denominator + denominator * t.numerator; temp.denominator = denominator * t.denominator; temp.simplify(); return temp; }
Rational::operator double() { return static_cast<double>(numerator) / denominator; }
ostream &operator<<(ostream &output, Rational &t) { output << t.numerator << '/' << t.denominator; return output; }
|
operator+
:实现有理数加法。创建一个临时
Rational
对象
temp
,计算分子和分母的和并简化。
operator double()
:将 Rational
对象转换为
double
类型,即分子除以分母的结果。
- 输出运算符重载
operator<<
:用于输出
Rational
对象的分数形式。
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void main() { Rational a; cout << double(a); cout << endl;
Rational b(2, 0); b.showFraction(); cout << endl;
Rational c(3, 4); c.showFraction(); cout << endl;
Rational d(1.2); d.showFraction(); cout << endl;
a = b + c; cout << double(a); cout << endl; }
|
- 创建不同类型的
Rational
对象并进行测试:
a
使用默认构造函数。
b
尝试使用非法分母 0
创建对象,并输出其结果。
c
使用合法的分数创建对象,并输出其结果。
d
使用双精度浮点数创建对象,并输出其结果。
- 将
b
和 c
相加,并输出其结果。
错误与改进
main
函数应该返回 int
类型,而不是
void
。
showFraction
函数在类定义中没有定义,需要补充其定义。
1 2 3
| void Rational::showFraction() const { cout << numerator << '/' << denominator; }
|
- 修改
main
函数的签名,并确保返回 0
以表示程序正常结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int main() { Rational a; cout << double(a); cout << endl;
Rational b(2, 0); b.showFraction(); cout << endl;
Rational c(3, 4); c.showFraction(); cout << endl;
Rational d(1.2); d.showFraction(); cout << endl;
a = b + c; cout << double(a); cout << endl;
return 0; }
|
这样改进后,程序可以正常编译和运行,并正确显示每个有理数对象的值及其运算结果。
***
2. (每小题 3 分,共 18 分)
在下面的程序中,类 B1
和 B2
虚继承类
A
,类 C
多重继承 B1
和
B2
。由于 A
是虚基类,创建间接派生类对象时,只有一个 A
对象的数据成员版本。请根据题目要求作答。注意:A
为虚基类。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <iostream>
using namespace std;
class A { public: double x, y; A(double px = 1, double py = 1) : x(px), y(py) {} virtual ~A() {} virtual void show() { cout << "[A ]=>" << x << "," << y << endl; } };
class B1 : virtual public A { public: B1(double px = 2, double py = 2) : A(px, py) {} void show() override { cout << "[B1]=>" << x * y << endl; } };
class B2 : virtual public A { public: double z; B2(double px = 3, double py = 3, double pz = 3) : A(px, py), z(pz) {} void show() override { cout << "[B2]=>" << 0.5 * (x + y) * z << endl; } };
class C : public B1, public B2 { public: double h; C(double px = 4, double py = 4, double pz = 4, double ph = 4) : A(px, py), B1(px, py), B2(px, py, pz), h(ph) {} void show() override { cout << "[C ]=>" << x << "," << y << "/" << x * y * h << "/" << 0.5 * (x + y) * z * h << endl; } };
int main() { A a; B1 b1; B2 b2; C c;
A* p = &a; p->show(); p = &b1; p->show(); p = &b2; p->show(); p = &c; p->show();
return 0; }
|
运行结果
1 2 3 4
| [A ]=>1,1 [B1]=>4 [B2]=>9 [C ]=>4,4/64/96
|
解释
- 类
A
的构造函数:
- 默认构造函数
A(double px=1, double py=1)
初始化
x
和 y
为 1
。
a
对象使用默认构造函数,所以 a
的
x
和 y
均为 1
。
- 调用
p->show()
输出 [A ]=>1,1
。
- 类
B1
的构造函数:
B1(double px=2, double py=2)
初始化 x
和
y
为 2
。
b1
对象使用 B1
的默认构造函数,所以
b1
的 x
和 y
均为
2
。
- 调用
p->show()
输出 [B1]=>4
(2 * 2 = 4
)。
- 类
B2
的构造函数:
B2(double px=3, double py=3, double pz=3)
初始化
x
、y
和 z
为
3
。
b2
对象使用 B2
的默认构造函数,所以
b2
的 x
和 y
均为
3
,z
为 3
。
- 调用
p->show()
输出 [B2]=>9
(0.5 * (3 + 3) * 3 = 9
)。
- 类
C
的构造函数:
C(double px=4, double py=4, double pz=4, double ph=4)
初始化 x
、y
为 4
,z
为 4
,h
为 4
。
c
对象使用 C
的默认构造函数,所以
c
的 x
和 y
均为
4
,z
为 4
,h
为
4
。
- 调用
p->show()
输出 [C ]=>4,4/64/96
:
4,4
是 x
和 y
的值。
x * y * h = 4 * 4 * 4 = 64
。
0.5 * (x + y) * z * h = 0.5 * (4 + 4) * 4 * 4 = 96
。
通过虚继承,类 C
只有一个基类 A
的实例,所以所有的 x
和 y
都指向同一个
A
的成员变量。
·写出上述程序的运行结果。
略
·在类 A 的 show 函数之前加上关键字
virtual,再写出上述程序的运行结果。
和上面应该没有差别
·在(28)题基础上,于原 main 函数的末尾添加 ((B2)c).show( );
结果将多显示一行: (29)
·把类 A 的 show 函数改写成纯虚函数。
·若类 A 的 show 函数改写成纯虚函数,原来的 main
函数不能正常运行,为什么?请简单
说明理由。
- 将类 A 的 show 函数改写成纯虚函数后,即改为
virtual void show() = 0;
,原来的 main
函数不能正常运行。因为类 A 的对象不能再被实例化,而 main 函数中使用了类
A 的对象进行实例化。
·类 A 的 show 函数改写成纯虚函数后,请你对原来的 main
函数作简单的删改,使其可以
输出派生类的数据。
- 类 A 的 show 函数改写成纯虚函数后,原来的 main
函数不能正常运行的原因是类 A
变成了抽象类,无法创建其对象。为了使程序正常运行,可以修改 main
函数,使用派生类的对象进行实例化,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int main() { A a; B1 b1; B2 b2; C c;
A* p1 = &b1; A* p2 = &b2; A* p3 = &c;
p1->show(); p2->show(); p3->show(); ((B2)c).show();
return 0; }
|
三、给出一些关于使用模板的程序段,按要求作答。
1.下列程序用函数模板实现两个数据的交换,请把有关的语句填充完整。
(每小题 3 分,共 9 分)
知识:
这道题考察了函数模板的使用。函数模板是一种通用的函数定义,可以在不指定具体数据类型的情况下定义函数,使得函数可以接受任意类型的参数。在这个例子中,我们使用了函数模板
swap
来实现两个数据的交换操作。
总结起来,函数模板的使用需要注意以下几点:
- 函数模板的声明以关键字
template
开始,后面跟有模板参数列表,用尖括号 < >
括起来,参数列表中可以包含一个或多个模板参数。
- 模板参数可以是任意有效的数据类型,包括基本数据类型(如
int
, double
, char
等)、类类型、指针类型等。
- 在函数模板定义中,使用模板参数来定义函数的参数类型、返回类型等,使得函数可以处理不同类型的数据。
- 在调用函数模板时,编译器会根据实际参数的类型来推断模板参数的具体类型,并生成对应的函数实例。
- 函数模板可以和普通函数一样进行重载,可以定义多个相同名字的函数模板,只要它们的参数列表或模板参数列表不同即可。
- 函数模板可以用于各种类型的参数,包括基本数据类型、用户自定义类型、指针类型等,提高了代码的通用性和重用性。
在实际编程中,函数模板通常用于编写通用的算法函数,例如容器类的排序、搜索等操作,以及数据类型无关的算法实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<iostream.h>
template <class T>
void swap(T& a, T& b) { T temp = a; a = b; b = temp; }
void main() { int j=1,k=3; cout<<"int 数据类型:\n"<<j<<","<<k<<"=>"; swap(j,k); cout<<j<<","<<k<<endl;
double x=1.23,y=9.87; cout<<"double 数据类型:\n"<<x<<","<<y<<"=>"; swap(x,y); cout<<x<<","<<y<<endl;
char p='A',q='B'; cout<<"char 数据类型:\n"<<p<<","<<q<<"=>"; swap(p,q); cout<<p<<","<<q<<endl ; }
|
2.下列程序使用了向量
vector 和算法 sort 实现数组的排序,请把有关的语句填充完整。
(每小题 3 分,共 9 分)
应该很容易,如果打过acm或者说学过一点点algorithm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| using namespace std;
const int size = 10;
void display(vector<int> V, int n) { int i; for (i = 0; i < n; i++) cout << V[i] << " "; cout << endl; }
bool down(int x, int y) { return x > y; }
int main() { int a[size] = {10, 3, 17, 6, 15, 8, 13, 34, 25, 2};
vector<int> V(a, a + size);
cout << "输出原始数组: \n"; display(V, size);
sort(V.begin(), V.end());
cout << "输出升序排列后的数组: \n"; display(V, size);
sort(V.begin(), V.end(), down);
cout << "输出降序排列后的数组: \n"; display(V, size);
return 0; }
|
四、给出一个输入输出流操作的程序段,请把有关的语句填充完整。
(每小题 3 分,共 6 分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <fstream> #include <cstdlib>
using namespace std;
int main() { ofstream outstuf; outstuf.open("e:\\newfile.dat", ios::app);
if(!outstuf) { cerr << "Error!" << endl; abort(); }
outstuf << "This is a file of example.\n";
outstuf.close();
return 0; }
|