一.现有理数的加法运算。请按后列要求作答。

  1. 仔细、完整地阅读程序,对应题号填写注释。注释的内容包括两部分:1.本行的语句是什么;2.简述其功能或特点。例如,注释可以这样写:“友员函数;重载加法运算符(+)。”
#include<iostream.h> 
#include<math.h>
#include<iomanip.h>

class Rational
{
private:
int numerator,denominator; //(例)数据成员;表示一个有理数的分子、分母。 int maxcommonFactor(int,int); //*(1)* 私有成员函数;求两个整数的最大公因子。
void simplify( ); //*(2)*
void DtoF(double,int&,int&); //*(3)*
public:
Rational(int n,int d=1); //*(4)*构造
Rational(double x=0); //*(5)*小数构造,需要转换
~Rational(); //*(6)* 析构函数
void plus(const Rational&); //*(7)*
Rational operator + (const Rational&); //(例)公有成员函数;重载加法运算符(+)。
void showFraction( );//*(8)* 打印
operator double( ); //类型转换
friend ostream &operator\<\< (ostream &output, Rational &t);重载
};

这题有些挠谭,只写了一点

  1. 对应题号把函数的定义补充完整。
int Rational::maxcommonFactor(int a,int b)

{
if(a%b==0)
return b;
return *(11)* ;-----------maxcommonFactor(b,a%b) ;
}
void Rational::simplify( )
{
int temp=maxcommonFactor(abs(numerator),abs(denominator));
if(temp==1)
return;
numerator/=temp;
denominator/=temp;
return;
}
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
{
(12) ; ;-------------------正常赋值即可
}
simplify();
}

Rational::Rational(double x)

{
DtoF(x,numerator,denominator);
simplify();
}
Rational::~Rational( ){ }

void Rational::plus(const Rational& addend)

{
numerator=addend.numerator\*denominator+addend.denominator\*numerator;
denominator\*=addend.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;
}
void Rational::showFraction( )

{
if(numerator\<0 && denominator\<0 \|\| numerator\>=0 && denominator\>0)
cout\<\<abs(numerator)\<\<"/"\<\<abs(denominator);

else
cout\<\<"-"\<\<abs(numerator)\<\<"/"\<\<abs(denominator);
}
Rational::operator double()

{
return *(13)* ;----------------------------------double(numerator)/double(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;
}

只是补充的话就比较简单了,你一定会

  1. 根据题目要求,按对应题号作答。

(14)写出上述程序的运行结果。

0
错误! 分母不能为0。有理数将置为0。
0/1
3/4
6/5
0.75

在上述程序中,使用成员函数重载加法运算符(+)。现假设要改用友员函数重载加法运算符(+),下面给出了函数体,请写出相应的函数原型(声明)和函数头。

函数原型 (15)

friend Rational operator + (const Rational &t1,const Rational &t2);

函数头 (16)

Rational operator + (const Rational &t1, const Rational &t2)

函数体

{
Rational temp(0,1); temp.numerator=t1.numerator\*t2.denominator+t1.denominator\*t2.numerator;
temp.denominator=t1.denominator\*t2.denominator;
temp.simplify(); return temp;
}

在上述程序中,构造函数出现重载,我们按其定义的先后分别称为构造函数 1 和构造函数 2。注意到main 函数中依次定义了对象a,b,c,d。请你分别写出这四个对象的初始化各自调用的是哪一个构造函数。

对象 a 初始化: 构造函数2

对象 b 初始化: 构造函数1

对象 c 初始化: 构造函数1;

对象 d 初始化: 构造函数2

在上述程序中,请你补充一个复制构造函数的定义,要求用内联函数实现。

(18)

Rational(const Rational& r) 
{
numerator=r.numerator;
denominator=r.denominator;
}

复制构造函数和重载赋值运算符函数虽然都是实现数据成员的复制,但执行时机不同。复制构造函数通常用于初始化对象和传递对象参数,当函数返回类类型时,也要通过复制构造函数建立临时对象;重载赋值运算符函数用于程序运行时修改对象的数据 。

·假设在上述程序的基础上,于main 函数末尾处添加一行:

for(int i=0;i<=1;i++)
c.plus(c+d);
cout<<double(c)<< ','<< double(d)<<endl;

写出这段语句的运行结果。

(20)6.6,1.2

二、给出一些类的定义,按要求作答。

  1. (每小题 3 分,共 18 分)
//类 A 
#include\<iostream.h\>
class A
{
private: double x;
public:
A(double px=1):x(px)
{ }
double getx( ) const
{
return x;
}
void show( )
{
cout\<\<"x="\<\<x\<\<endl;
}
} ;
//类 B 私有继承类 A,类 B 的定义及 main 函数如下:
class B: private A
{
private: double x;
public :
B(double px=2): x(px){ } //类 B 构造函数定义
void show( )
{
cout\<\<getx( )\<\<endl;
}
} ;
void main( )
{
A a;
a.show( );
B b(3);
b.show( );
}

·执行上述程序,运行结果为:

(21)

x=1
1

若把类B 构造函数定义改为 B(double px=2):A(px){ } ,则程序运行结果为:

(22)

x=1
3

改写上述程序中的类B 构造函数定义 B(double px=2): x(px){ }。要求不使用“初始化式”,但函数的功能不变,且内联函数的形式也不变。按要求,类 B 构造函数定义可以改写成:

(23)

B(double px=2)
{
x=px;
}

有时需要复制 A 类对象的数据去创建 B 类对象,请用内联函数的形式写出类 B 的重载构造函数:

(24)

B(const A& a)
{
x=a.getx( );
}

假设在上述main 函数的末尾添加一句:cout<<b.getx( )<<endl;getx( )私有,不可访问。请你在类 B 的定义中作一声明,保持getx( )仍为公有,使添加的语句可以运行。该声明为:

(25)

A::getx;
Public继承:
基类的 public 成员在派生类中仍然是 public。
基类的 protected 成员在派生类中仍然是 protected。
基类的 private 成员在派生类中不可访问。
Protected继承:
基类的 public 成员在派生类中变为 protected。
基类的 protected 成员在派生类中仍然是 protected。
基类的 private 成员在派生类中不可访问。
Private继承:
基类的 public 成员在派生类中变为 private。
基类的 protected 成员在派生类中变为 private。
基类的 private 成员在派生类中不可访问。
使用 using 声明可以将基类的成员引入派生类的作用域中,从而改变它们在派生类中的访问权限。对于 private 继承特别有用,因为它可以将本来在 private 继承中变为 private 的成员重新提升到 public 或 protected。
在C++中,只有基类中的public和protected成员才能通过using声明在派生类中引入,并使其在派生类的作用域中变为public或protected,从而可以在类外访问。基类中的private成员是不能通过这种方式在派生类中引入和访问的。

在类 B 公有段增加一个内联函数fetch的定义,其功能是返回 B 类对象数据 x 的平方值。

(26)

double fetch()
{
return x*x;
}
  1. (每小题 3 分,共 18 分)

下列程序中,类 $B1,B2$ 虚继承类 A,类 C 多继承 $B1,B2$。按题目要求作答。注意 A 为虚基类,建立间接派生类对象时,只有一个 a 对象的数据成员版本。

#include<iostream.h>
class A //长度 x,y
{
public:
double x,y;
A(double px=1,double py=1):x(px),y(py){ }
virtual ~A( ){ }
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()
{
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()
{
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):B2(px,py,pz),h(ph){}
void show()
{
cout\<\<"\[C \]=\>"\<\<x\<\<","\<\<y\<\<"/"\<\<x\*y\*h\<\<"/"\<\<0.5\*(x+y)\*z\*h\<\<endl;
}
} ;
void 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( );
}

写出上述程序的运行结果。

(27)

[A ]=>1,1
[A ]=>2,2
[A ]=>3,3
[A ]=>1,1
此处为虚拟继承,前三步一致,最后一步因为虚拟继承的特性,b1,b2会共用一份a,从层次结构上,a脱离于c的构造函数而提前构造,b1,b1在层次结构上和c连接,因此会跟随c的构造函数提前构造。
当不使用虚拟继承时,就会恢复为全部依靠c的构造函数而一步步提前构造的情况。
此外,由于未使用virtual修饰show,所以所有的a的指针都会指向该对象中的a的show。

在类A 的show 函数之前加上关键字 virtual,再写出上述程序的运行结果。

(28)

[A ]=>1,1
[B1]=>4
[B2]=>9
[C ]=>1,1/4/16

在(28)题基础上,于原 main 函数的末尾添加 ((B2)c).show( ); 结果将多显示一行:

(29)

[B2]=>4
子类到父类的强制类型转换就像大瓶子的水装到小瓶子中,必然会损失一些数据,这些数据就是子类不同于父类的一部分

·把类A 的show 函数改写成纯虚函数。

(30)

若类 A 的show 函数改写成纯虚函数,原来的 main 函数不能正常运行,为什么?请简单说明理由。

类A的show函数改写成纯虚函数,具有纯虚函数的类A成为抽象类,而抽象类是不能建立对象的。原来的main函数中,有语句“A a;” 即对类A建立对象a,因此不能正常运行。
解释下抽象类和纯虚函数。
1.其子类必须重写纯虚函数,否则会出现类似于指针悬挂的情况。
2.虚拟继承或是其他任何继承都不会影响抽象类的使用
3.任何方式的实例化抽象类都是不允许的,除非在子类的构造函数中以构造的形式,就像是子类的重写接口未对上,导致纯虚函数的指针悬挂了一样

(31)

类 A 的show 函数改写成纯虚函数后,请你对原来的 main 函数作简单的删改,使其可以输出派生类的数据。

(32)

B1 b1; B2 b2; C c;
A* p=&b1; p->show();
p=&b2; p->show();
p=&c; p->show();

三、给出一些关于使用模板的程序段,按要求作答。

  1. 下列程序用函数模板实现两个数据的交换,请把有关的语句填充完整。
#include<iostream> 
template <(33)>--------typename T

void swap(*(34)* ) -------T&a,T&b
{*(35)* } ------------------T c=a;a=b;b=a;
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 ;

}
引入模板
  1. 下列程序使用了向量 vector 和算法 sort 实现数组的排序,请把有关的语句填充完整。
#include<iostream> 
#include<vector>
#include<algorithm>
using *(36)* ;---------namespace std;
const int size=10;
void display(vector\<int\>V,int n)
{
int i;
for(i=0;i\<size;i++)
cout\<\<*(37)* \<\<" ";---------V[i];
cout\<\<endl;
}
bool down(int x,int y)
{
return *(38)* ;-------------------x>y
}
void 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);
}

四、给出一个输入输出流操作的程序段,请把有关的语句填充完整。

#include <iostream.h\> 
#include <fstream.h\>
#include <stdlib.h\>
void main( )
{
ofstream outstuf ;
// 建立输出文件流对象
outstuf.open("e:\\newfile.dat",*(39)* ); ------------ios::out
if(!outstuf)
{
cerr\<\<"error!"\<\<endl;abort( );
}
// 调用重载算符函数测试流 outstuf\<\<"This is a file of example.\n"; // 写入一行内容

outstuf *(40)* ;-------------.close()

}

总结

本试卷考察:模板,文件,继承(虚拟继承)