2022C++试卷
2022C++试卷
一.单项选择题(每小题 2 分,共 20 分)
1.在 C++ 中,数据与操作的封装是借助于 ____B______ 达到的。
- 指针 (B) 类 (C) 数组 (D) 函数
(B) 类。在C++中,数据与操作的封装是通过类实现的。类将数据和相关的操作(成员函数)封装在一起,形成一个完整的对象,从而实现数据的隐藏和操作的封装。
2.下面叙述不正确的是 _____C_____ 。
- 基类的保护成员在派生类中仍然是保护的
- 基类的保护成员在公有派生类中仍然是保护的
- 基类的保护成员在私有派生类中仍然是保护的
- 对基类成员的访问必须是无二义性的
在私有派生类中,基类的保护成员会变成私有成员,而不是保持为保护成员。这使得基类的保护成员对于派生类以外的代码来说是不可见的。所以正确答案是:(C) 基类的保护成员在私有派生类中不再是保护的,而是变为私有成员。
3.以下基类中的成员函数中表示纯虚函数的是 _______B___ 。
virtual void vf(int); (B) virtual void vf(int)=0;
virtual void vf(int=0); (D) virtual void vf(int){ };
表示纯虚函数的语法是在成员函数声明的结尾处使用
= 0
,因此选项 (B)virtual void vf(int)=0;
是正确的。这表示vf
是一个纯虚函数,派生类必须提供其自己的实现。其他选项都不是纯虚函数的声明方式。
4.下面对类的析构函数的定义是 ______D____ 。
X::~ X( 参数 ); (B) void X::~ X( 参数 );
void X::~ X(); (D) X::~ X();
正确的类析构函数的定义是 (D)
X::~X();
。在类的定义中,析构函数不需要返回类型,并且不应指定任何参数。因此,选项
(D) 是正确的。
5.下列关于运算符重载的描述中, ___D_______ 是正确的。
- 运算符重载可以改变操作数的个数。
- 运算符重载可以改变优先级。
- 运算符重载可以改变结合性。
- 运算符重载不可以改变原语法规则。
正确的描述是:(D) 运算符重载不可以改变原语法规则。
运算符重载允许程序员重新定义现有的运算符以用于用户自定义类型。尽管运算符重载允许对操作数的数量进行改变(例如一元运算符重载),但是它不能改变运算符的优先级或结合性。运算符重载必须遵循原始的C++语法规则,因此它不能改变这些规则。
6.编译时的多态性通过使用 ____B______ 实现。
- 构造函数 (B) 虚函数 (C) 重载函数 (D) 析构函数
编译时的多态性通过使用虚函数(B)来实现。虚函数允许在运行时动态地确定调用的函数版本,这就是多态性的一种形式。在编译时,编译器可能无法确定到底应该调用哪个版本的函数,因此它会生成一个虚函数表来管理各个类的虚函数地址,在运行时根据对象的实际类型来调用正确的函数版本。
7.下面对类的构造函数的定义是 ___B_______ 。
void X::X( 参数 ) (B) X::X( 参数 )
int X::X( 参数 ) (D) float X::X( 参数 )
很明显...
正确答案是 (B) X::X( 参数 )。在C++中,类的构造函数的定义通常是在类的声明之外,使用类名加上作用域解析运算符(::)来定义。因此,构造函数的定义应该是 X::X(参数)。
8.已知类 A 中的一个成员函数说明如下: void Set( A & a ) 其中, A & a 的含义是 __C________ 。
- 指向 A 类的指针为 a
- 将 a 的地址值赋给变量 Set
- a 是类 A 的引用,用来做函数 Set() 的形参
- 变量 A 和 a 是函数 Set() 的两个形参
正确答案是 (3) a 是类 A 的引用,用来做函数 Set() 的形参。在函数声明
void Set(A &a)
中,A &a
表示函数Set()
接受一个引用参数,该引用参数的类型是类A
。这意味着在调用函数Set()
时,需要提供一个类A
的对象作为参数,并且这个参数是通过引用传递的,而不是通过值传递。
9.下列函数中, _______D___ 是对文件进行写操作的。
- get() (B) read() (C) seekg() (D) put()
正确答案是 (D) put()。函数
put()
是用于向文件中写入字符的函数,因此它是对文件进行写操作的函数。
get()
用于从文件中读取字符。read()
用于从文件中读取指定数量的字节。seekg()
用于设置文件读取位置。put()
用于向文件中写入字符。
当我们在C++中处理文件时,有一些基本的文件操作函数可以用来读取和写入文件。以下是这些函数的简要说明和示例:
get()
:用法:
get()
函数从输入流中获取一个字符,并将其作为返回值返回。示例:
1
2char ch;
ch = cin.get(); // 从标准输入获取一个字符
read()
:用法:
read()
函数从文件中读取指定数量的字节,并将其存储到指定的缓冲区中。示例:
1
2
3char buffer[100];
ifstream file("example.txt", ios::binary);
file.read(buffer, sizeof(buffer)); // 从文件中读取指定数量的字节并存储到缓冲区中
seekg()
:用法:
seekg()
函数用于设置文件读取位置。示例:
1
2ifstream file("example.txt");
file.seekg(10, ios::beg); // 将文件读取位置设置到文件开头后的第10个字节处
put()
:用法:
put()
函数向输出流中写入一个字符。示例:
1
2char ch = 'A';
cout.put(ch); // 向标准输出流中写入字符 'A'
这些函数是对C++中常见的文件读写操作中的一部分。在实际应用中,根据需要,可能会使用其他函数进行更复杂的文件操作。
10.写一个 C++ 文件,要创建一个 ______B____ 流对象。
- ifstream (B) ofstream (C) cin (D) cout
正确答案是 (B) ofstream。
在C++中,要创建一个文件流对象来进行文件写操作,我们需要使用
ofstream
类。这个类提供了一些成员函数来方便地向文件中写入数据。例如,我们可以使用ofstream
对象的open()
方法来打开一个文件,然后使用<<
操作符来写入数据到文件中。
示例代码:
1 |
|
在上面的示例中,我们创建了一个名为 "example.txt" 的输出文件流对象
outputFile
,然后使用 <<
操作符将数据写入到这个文件中。 ***
二.程序填空题( 30 分)
1.
1 | 1. |
输出: 6 h
这段代码中存在两个类:A
和 B
。类
A
公有继承类 B
,而类 B
则私有继承类 A
。
在类
A
中,有两个成员函数f
和g
。f
函数接受一个整数参数并输出,g
函数输出字符串 "g"。在类
B
中,定义了一个新的成员函数h
,用于输出字符串 "h"。在类B
的定义中,通过A::f;
的方式使用了类A
中的成员函数f
。
在 main
函数中,首先创建了一个类 B
的对象
d
。然后通过对象 d
调用了成员函数
f
和 h
,分别输出了整数 6
和字符串
"h"。由于类 B
私有继承了类 A
,因此在类
B
的成员函数和外部代码中都无法直接访问类 A
的成员函数 g
。
2.
Sure, here's your code in a more readable format:
1 |
|
一眼了()
输出: 20 30 10
这段代码定义了一个类 p_class
,其中包含了一个成员变量
num
和两个成员函数 set_num
和
show_num
。set_num
函数用于设置成员变量
num
的值,而 show_num
函数用于输出成员变量
num
的值。
在 main
函数中,首先创建了一个 p_class
类型的数组 ob
,包含了3个对象。然后通过循环为每个对象的
num
成员赋值。接下来有三行代码需要填空:
- 在第一个空白处,将指针
p
指向数组ob
的第二个元素。 - 在第二个空白处,调用
p
指针指向对象的show_num
函数,输出该对象的num
值。 - 在第三个空白处,将指针
p
指向数组ob
的第三个元素。
最后分别输出了数组中每个对象的 num
值。
3.
1 |
|
这段代码定义了一个名为 Tdate
的类,表示日期。该类包含了三个私有成员变量
month
、day
和
year
,以及一个成员函数 IsLeapYear()
用来判断年份是否为闰年。
在 main
函数中,首先创建了一个名为 a
的
Tdate
类型的对象,并调用其 Set
函数来设置日期为 2 月 4 日 2000 年。然后调用对象的 Print
函数来输出日期信息和判断该年是否为闰年。
在代码中有三个空白处需要填充:
在第一个空白处,应该是
void Print();
,声明Print
函数。在第二个空白处,应该是
void Tdate::Print()
,定义Print
函数的类作用域。在第三个空白处,应该是
cout << month << "/" << day << "/" << year << endl;
,输出日期信息。然后在下一行判断是否为闰年并输出相应信息。
输出结果应该是:
1
22/4/2000
This year Is leapyear.
4.
1 |
|
输出: 8:30PM
20:30
10:45AM
10:45
这个程序定义了一个 Time
类,该类用于表示时间。在
Time
类中,有两个构造函数,一个用于初始化小时和分钟,另一个用于打印时间。其中,构造函数
(9)
初始化了小时和分钟,构造函数 (10)
是一个静态函数,用于打印以 24 小时制表示的时间。
在 Time
类外部,定义了两个函数,分别用于打印时间以 12
小时制和 24 小时制。这两个函数都接受一个 Time
类型的参数,用于表示时间。
在 main()
函数中,创建了两个 Time
类的对象
T1
和 T2
,并分别调用了以 12 小时制和 24
小时制表示时间的函数进行打印。
5.
1 |
|
这个其实应该也是一眼题罢
输出:
class A class B class C class D
这段代码定义了四个类 A、B、C 和 D。类 A 具有一个构造函数和一个析构函数,用于输出字符串。类 B 和类 C 分别从类 A 继承,并在构造函数中调用了类 A 的构造函数。类 D 继承了类 B 和类 C,并在构造函数中调用了所有父类的构造函数。在主函数中创建了类 D 的一个对象,并释放了内存。
知识点:
这道题考察的是多继承中的虚拟继承和构造函数的调用顺序。在 C++ 中,当一个类通过多个路径继承自同一个基类时,可能会导致基类的构造函数被多次调用,从而产生冗余或错误的行为。为了解决这个问题,可以使用虚拟继承。
在这个例子中,类 B 和类 C 都继承自类 A,而类 D 继承自类 B 和类
C。如果不使用虚拟继承,那么类 D 将会有两个独立的类 A 子对象,分别来自类
B 和类 C,导致类 A
的构造函数被调用两次,产生冗余的输出。为了避免这种情况,我们使用了虚拟继承,通过在类
B 和类 C 继承类 A 时加上关键字 virtual
,这样在类 D
中就只会有一个共享的类 A 子对象。
构造函数的调用顺序如下:
- 首先调用类 A 的构造函数,输出 "class A"。
- 接着调用类 B 的构造函数,输出 "class B"。
- 然后调用类 C 的构造函数,输出 "class C"。
- 最后调用类 D 的构造函数,输出 "class D"。
使用虚拟继承和适当的构造函数调用顺序确保了每个类的构造函数只被调用一次,避免了冗余的输出。
三.读程序写运行结果 (24 分)
1.
1 |
|
这段代码定义了两个类 desk
和
luxury_desk
,其中 luxury_desk
是
desk
的子类。desk
类表示普通的桌子,有三个私有成员变量
length
、width
和
high
,以及两个公有成员函数 volume()
和
price()
,分别用于计算桌子的体积和价格。luxury_desk
类继承了 desk
类,并添加了一个私有成员变量
m_price
表示桌子的价格系数,并重写了 price()
函数来计算豪华桌子的价格。
在 main()
函数中,创建了三个桌子对象
da
、da1
和
db
,分别是普通桌子、带参数的普通桌子和豪华桌子。然后分别输出它们的体积和价格。
在C++中,iostream.h
头文件已经不再使用,正确的头文件应该是#include <iostream>
1 |
|
这段代码创建了三个桌子对象,da
是普通桌子对象,da1
是带参数的普通桌子对象,db
是豪华桌子对象。然后输出它们的体积和价格。
输出结果应为:
60 120 60 150 6 12 *** ### 2.
在这段代码中,定义了两个类B和D。类B有一个整型成员b和一个虚函数virfun(),它输出b的值。类D继承自类B,有一个额外的整型成员d,并且重写了虚函数virfun(),在输出b的值后,还输出了d的值。 函数fun()接受一个B类指针作为参数,并调用它的虚函数virfun()。 在主函数main()中,创建了一个D类对象pd,它的构造函数将3和5传递给基类构造函数和类D的私有成员d。然后调用fun()函数,将pd作为参数传递给它。根据多态性,尽管参数是一个基类指针,但实际上会调用类D的virfun()函数。
1 |
|
输出应为:
1 | B::b: 3 , D::d: 5 |
正常知道一下构造函数的执行顺序即可做 ***
3.
在给出的代码中,定义了一个Boolean类,其成员变量是一个枚举类型BoolConst的变量logic,表示逻辑值True或False。类中包含一个构造函数和一个打印函数print(),以及两个友元重载运算符+和。 重载运算符+和分别实现逻辑与和逻辑或的功能。
1 |
|
输出应为:
1 | FALSE TRUE FALSE TRUE |
4.写出 data.txt 中的结果和屏幕显示的结果。
1 |
|
文件 data.txt 的内容应为:
1 | string |
屏幕显示的结果应为:
1 | x=50.5, a= 20 |
在文件 data.txt 中,首先写入了一个字符串 "string",接着是整数 a 的值加上 10,再接着是浮点数 x 的值。然后从文件中读取字符串、整数和浮点数,分别存入变量 str、a 和 x 中,并在屏幕上输出。 *** ## 四.改错题。找出以下程序的错误,并注释错误原因。(10 分)
1.
1 | class Time |
在提供的代码中,有几个错误:
- 构造函数
Time
的第二个定义拼写错误,应该是Time
而不是Timt
。 - 构造函数
Time
的第一个定义声明了一个参数day
,但第二个定义没有声明参数day
,应该统一参数列表。 - 在
main
函数中,试图访问Time
类的私有成员iMonth
,但是私有成员无法在类外部直接访问。
修复后的代码应该如下所示:
1 | class Time { |
在修复后的代码中,已经删除了 main
函数中试图访问私有成员
iMonth
的语句,并修正了构造函数的定义,确保参数列表一致。
*** ### 2.
1 | Template<class T> |
在这段代码中,错误在于使用了未定义的 Type
。应该使用
T
作为返回类型,而不是 Type
。
main()
函数应该返回 int
类型。
还有一个转换问题。 隐式实例化
:让编译器根据实参推演模板参数的实际类型 1
2
3
4
5
6
7
8
9
10
11
12
13template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2); //编译器推出T是int
Add(d1, d2); //编译器推出T是double
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, d1); //err 编译器推不出来
/*
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有
一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
*/
}
修正后的代码应该如下所示:
1 |
|
五.完成程序。(共 16 分)
1. 一个类的头文件如下所示:
test.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class test
{ private: int num;
public: test(int);
void show();
};
test::test(int n) { num = n; }
void test::show() { cout<<num<<endl;}
编写一个主程序,产生对象 TTT,令 TTT 数据成员的值为 5,并使用 show()函数输出这个 对象的数据成员。
以下是编写的主程序,用于测试 test
类:
1 |
|
这个主程序包含了头文件 "test.h"
,因此可以使用
test
类。在 main()
函数中,创建了一个名为
TTT
的 test
类对象,并传入值为
5。然后调用了对象的 show()
函数,用于输出对象的数据成员。
*** ### 2. 根据下面的主程序,补上所缺的类说明文件的最小形式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main()
{
base stry;
stry.init(6); //对数据成员赋值
cout<<stry.Getnum(); //输出数据成员的值
} base
的类,并且提供必要的成员函数和数据成员来实现其功能。由于主程序中使用了
init()
和 Getnum()
函数,因此类
base
需要包含这两个函数。
下面是补充完整的类说明文件 base.h
:
1 |
|
这个文件定义了一个名为 base
的类,其中包含了一个私有数据成员 num
,表示整数值。类
base
还包含了两个公有成员函数 init()
和
Getnum()
。init()
函数用于初始化数据成员
num
,而 Getnum()
函数用于返回数据成员
num
的值。
在主程序中,首先创建了一个名为 stry
的 base
类对象。然后使用 init()
函数将其数据成员的值设为
6。最后,通过调用 Getnum()
函数输出 stry
对象的数据成员值。