中南大学考试试卷1

一、选择题(单选)(每空1分,共15分)

1、在C++中,用于实现动态多态性的是( )。

A.内联函数 B.重载函数 C.模板函数 D.虚函数

2、下列关于类和对象的叙述中,错误的是( )。

A.一个类只能有一个对象 B.对象是类的具体实例

C.类是对某一类对象的抽象 D.类和对象的关系是一种数据类型与变量的关系

3、关于友元函数的描述中,错误的是( )

A. 友元函数不是成员函数 B. 友元函数只能访问类中私有成员

C. 友元函数破坏隐藏性,尽量少用 D. 友元函数说明在类体内,使用关键字friend

4、下列运算符中,( )运算符在C++中不能被重载。

A.&& B.[] C.new D.? :

5、模板的使用实际上是将类模板实例化成一个( )。

A.函数 B.对象 C.类 D.抽象类

6、要求通过函数来实现一种不太复杂的功能,并且要求加快执行速度,选用( )。

A. 内联函数 B. 重载函数 C. 递归调用 D. 嵌套调用

7、下面对模板的声明,正确的是( )。

A. template B. template<class T1, T2>

C. template<class T1, class T2> D. template<class T1; class T2>

8、已知:print( )函数是一个类的常成员函数,它无返回值,下列表示中,( )是正确的。

A. void print( ) const; B. const void print( );

C. void const print( ); D. void print(const);

9、已知X类,则当程序执行到语句X array[3];时,调用了( )次构造函数。

A.0 B.1 C.2 D.3

10、假定TT为一个类,则该类的拷贝构造函数的声明语句为( )。

A.TT (TT x) B.TT& (TT x) C.TT (TT &x) D.TT (TT *x)

11、在表达式 x+y*z中, + 是作为成员函数重载的运算符,* 是作为非成员函数重载的运算符。则 operator+ 有 个参数,operator* 有 参数。( )

​ A. 2、2 B. 2、1 C. 1、1 D. 1、2

12、下面( )的叙述不符合赋值兼容规则。

A. 派生类对象可以赋值给基类对象 B. 基类对象可以赋值给派生类对象

C. 派生类对象可以初始化基类对象 D. 派生类对象的地址可以赋值给指向基类指针

13、已知:int m=10; 下列表示引用的方法中,( )是正确的。

A.int &x=m; B.int &y=10; C.int &z; D.float &t=&m;

14、不能说明为虚函数的是( )。

A.析构函数 B.构造函数 C.类的成员函数 D.以上都不对

15、下列情况中,哪一种情况不会调用拷贝构造函数 ( )

A.派生类对象去初始化基类对象时 B.函数返回值是类对象,函数返回时

C.函数形参是类对象,调用函数时 D.类对象赋值给该类的另一个对象时

解释如下: 以下是对这些选择题的答案和解释:

  1. 在C++中,用于实现动态多态性的是( )。
    • D.虚函数
    • 解释:虚函数(virtual functions)是用于实现动态多态性(即运行时多态性)的。在基类中声明虚函数,派生类可以重写这些虚函数,当通过基类指针或引用调用该函数时,将执行派生类的版本。
  2. 下列关于类和对象的叙述中,错误的是( )。
    • A.一个类只能有一个对象
    • 解释:一个类可以有多个对象。对象是类的实例,一个类可以实例化多个对象。
  3. 关于友元函数的描述中,错误的是( )
    • B. 友元函数只能访问类中私有成员
    • 解释:友元函数可以访问类的私有和保护成员,而不仅仅是私有成员。
  4. 下列运算符中,( )运算符在C++中不能被重载。
    • D.?:
    • 解释:三元条件运算符(?:)不能被重载。而其他运算符如 &&[]new 都可以被重载。
  5. 模板的使用实际上是将类模板实例化成一个( )。
    • C.类
    • 解释:类模板实例化的结果是一个类。函数模板实例化的结果是一个函数。
  6. 要求通过函数来实现一种不太复杂的功能,并且要求加快执行速度,选用( )。
    • A. 内联函数
    • 解释:内联函数通过在调用处展开代码,避免了函数调用的开销,从而加快执行速度。
  7. 下面对模板的声明,正确的是( )。
    • C. template<class T1, class T2>
    • 解释:模板声明中,多个模板参数之间用逗号分隔,并且每个模板参数都需要使用 classtypename 关键字。
  8. 已知:print( )函数是一个类的常成员函数,它无返回值,下列表示中,( )是正确的。
    • A. void print( ) const;
    • 解释const 修饰符放在函数签名的末尾,表示这是一个常成员函数。
  9. 已知X类,则当程序执行到语句X array[3];时,调用了( )次构造函数。
    • D.3
    • 解释:声明一个包含3个对象的数组时,会调用3次构造函数,分别构造每个对象。
  10. 假定TT为一个类,则该类的拷贝构造函数的声明语句为( )。
    • C.TT (TT &x)
    • 解释:拷贝构造函数的参数是当前类类型的引用,以避免无限递归调用拷贝构造函数。
  11. 在表达式 x+y*z中, + 是作为成员函数重载的运算符,*是作为非成员函数重载的运算符。则 operator+ 有 个参数,operator* 有 参数。( )
    • D. 1、2
    • 解释:成员函数形式的 operator+ 有一个参数(另一个操作数隐含为 this),非成员函数形式的 operator* 有两个参数。
  12. 下面( )的叙述不符合赋值兼容规则。
    • B. 基类对象可以赋值给派生类对象
    • 解释:基类对象不能赋值给派生类对象,因为派生类可能包含基类对象所没有的成员,无法进行安全的赋值。
  13. 已知:int m=10; 下列表示引用的方法中,( )是正确的。
    • A.int &x=m;
    • 解释:引用必须用已有的变量初始化,并且引用类型必须与被引用对象类型相同。
  14. 不能说明为虚函数的是( )。
    • B.构造函数
    • 解释:构造函数不能被声明为虚函数。析构函数和其他成员函数都可以声明为虚函数。
  15. 下列情况中,哪一种情况不会调用拷贝构造函数 ( )
    • D.类对象赋值给该类的另一个对象时
    • 解释:赋值操作调用的是赋值运算符,而不是拷贝构造函数。拷贝构造函数在对象初始化时(如函数传参、返回值)调用。

二、填空题(每空1分,共15分)

1. 后置自增运算符“++”重载为类的成员函数(设类名为A)的形式为: (1) 。

2. 若要把void fun(B &b)定义为类B的友元函数,则应在类定义中加入语句 (2) 。

3. 面向对象程序设计有4个主要特点: (3) 、 (4) 、(5) 、 (6) 。

4. 如果只想对公共基类构建一次,就必须使用关键字 (7) 把这个公共基类声明为虚基类。

5. img在C++中,三种继承方式的说明符号为 (8) 、 (9) 、 (10) ,如果不加说明,则默认的继承方式为 (11) 。

6. 如果需要在被调函数运行期间,改变主调函数中实参变量的值,则函数的形参应该是(12) 类型或 (13) 类型。

7. 在图1中,A,B,C,D,E,F均是类,其中属于单继承的派生类有(14) ,属于多继承的派生类有 (15)

这些填空题的答案是:

  1. (1)A operator++(int)
    • 这表示后置自增运算符 "++" 被重载为类 A 的成员函数形式。
  2. (2)friend void fun(B &b)
    • 这表示将 void fun(B &b) 定义为类 B 的友元函数,需要在类定义中加入该语句。
  3. (3)抽象 (4)封装 (5)继承 (6)多态性
    • 这些是面向对象程序设计的四个主要特点,它们可以以不同的顺序进行描述。
  4. (7)virtual
    • 这表示使用关键字 "virtual" 将公共基类声明为虚基类,以便只对其构建一次。
  5. (8)public (9)private (10)protected (11)private
    • 这些是 C++ 中三种继承方式的说明符,以及默认的继承方式。
  6. (12)指针 (13)引用
    • 这表示如果需要在被调函数运行期间改变主调函数中实参变量的值,函数的形参可以是指针类型或引用类型。
  7. (14)E (15)D,F
    • 这表示图中单继承的派生类为 E,多继承的派生类有 D 和 F。

三、判断题(每小题1分,共15分)

1. 纯虚函数是在基类中说明的虚函数,它在该基类中没有定义具体的操作内容。

2. 定义class时,用户自己必须定义构造函数。

3. 在C++中,定义结构体类型struct时不能有成员函数,只有class可以有成员函数。

4. 常对象只能调用它的常成员函数, 而不能调用普通的成员函数。

5. 友元函数说明在类体内,它是一种成员函数。

6. 构造函数的名字必须与类名相同,其返回类型缺省为void类型。

7. 运算符重载以后,其优先级可以改变。

8. 多继承提供了软件重用的强大功能,也增加了程序的复杂性。

9. 使用public公有继承时,基类中的私有成员在派生类中可以直接访问。

10. cin 是 istream 类的成员函数。

11. 类静态成员数据为该类所有对象共享,在该类对象被撤销时,静态成员并不撤销。

12. 在声明一个类时,必须同时声明类的数据成员和成员函数。

13. 析构函数的作用是当对象不用时,删除对象。

14. 运算符函数可以重载为成员函数,也可能重载为友元函数或者普通函数。

15. 数据流本身没有逻辑格式,数据的解释方式由应用程序的操作决定。

这些判断题的答案是:

  1. √ 纯虚函数是在基类中声明但没有定义具体操作内容的虚函数。
  2. × 定义类时,可以不定义构造函数。如果没有显式定义构造函数,编译器会生成默认构造函数。
  3. × 在 C++ 中,定义结构体 struct 类型时也可以有成员函数。
  4. √ 常对象只能调用它的常成员函数,不能调用非常成员函数。
  5. × 友元函数可以在类外定义,但在类内声明。它并不是类的成员函数。
  6. × 构造函数的名字必须与类名相同,但返回类型没有缺省值。
  7. × 运算符重载后,其优先级和结合性不会改变。
  8. √ 多继承提供了强大的软件重用功能,但也增加了程序的复杂性。
  9. × 使用 public 公有继承时,基类中的私有成员在派生类中是不可直接访问的。
  10. × cin 不是 istream 类的成员函数,它是一个全局对象。(不是函数而是对象)
  11. √ 类的静态成员数据在该类的所有对象之间共享,它们存在于类的整个生命周期中,不会因为类的对象被撤销而撤销。
  12. × 在声明一个类时,不需要同时声明类的数据成员和成员函数。它们可以在类的定义中分开声明和定义。
  13. × 析构函数的作用是在对象被销毁时执行必要的清理工作,而不是简单地删除对象。
  14. √ 运算符函数可以重载为成员函数、友元函数或普通函数。
  15. √ 数据流本身没有逻辑格式,数据的解释方式取决于应用程序的操作。

四、下列小程序各1个错误,指出错误行数,并进行改正(每小题4分,共8分)

原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1、类模板的对象定义  
① template <class T>
② class myAdd
③ {
④ T x,y;
⑤ public:
⑥ myAdd (T a,T b){ x=a, y=b;}
⑦ T sum( ){return x+y;}
⑧ };
⑨ myAdd (double) obj(5.3, 4.2);

2、包括静态函数和数据的类定义
① class myClass
② {
③ myClass ( ){ }
④ static void AddCount(){
⑤ this-> count ++;
⑥ }
⑦ private:
⑧ static int count;
⑨};

改正

1. 类模板的对象定义

1
2
3
4
5
6
7
8
template <class T>
class myAdd {
T x, y;
public:
myAdd(T a, T b) { x = a, y = b; }
T sum() { return x + y; }
};
myAdd<double> obj(5.3, 4.2);

()改成<> ***

2. 包括静态函数和数据的类定义

1
2
3
4
5
6
7
8
9
10
class myClass {
public:
myClass() {}
static void AddCount() {
// 静态成员函数无法使用 this 指针
// 错误行:this->count++;
}
private:
static int count;
};

静态成员函数没有this指针 ***

五、程序填空题:下列程序中缺少若干条语句,每条下划线只填一条语句(每条语句2分,共12分)

1. 下面是类Complex定义,其中重载的运算符<<输出复数表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
class Complex {
public:
Complex(double r, double i) { real = r; imag = i; }
friend ostream& operator<<(ostream&, Complex&);
private:
double real;
double imag;
};

ostream& operator<<(ostream& output, Complex& c) {
output << "(" << c.real << "+" << c.imag << "i)" << endl;
return output; // (1)填写语句
}

2. 定义数组类模板。

1
2
3
4
5
6
7
8
9
10
11
template<typename T>
class Array {
public:
Array(int s);
virtual ~Array();
virtual const T& GetAt(int index) const;

protected:
int size;
T* element;
};
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
template<typename T>
Array<T>::Array(int s) {
if (s > 1)
size = s;
else
size = 1;
element = new T[size]; // (3)填写语句
}

template<typename T>
Array<T>::~Array() {
delete[] element; // (4)填写语句
}

template<typename T>
const T& Array<T>::GetAt(int index) const {
return element[index];
}

class point {
int m, n;
public:
point(int, int);
point(point &);
};

point::point(int a, int b) {
m = a;
n = b; // (5)填写语句
}

point::point(point &t) {
m = t.m;
n = t.n; // (6)填写语句
}

六、简答题(每题3分,共12分)

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
#include <iostream.h>
class B;
class A
{
public:
A(int i)
{
a=i;
}
friend int F(A &f1,B &f2);
private:
int a;
};
class B
{
public:
B(int i)
{
b=i;
}
private:
int b;
};
int F(A &f1,B &f2)
{
return (f1.a+f2.b)* (f1.a-f2.b);
}
void main()
{
A n1(10);
B n2(8);
cout<<F(n1,n2)<<endl;
}

阅读上面的程序,简答以下4个问题:

① 写出程序的运行结果。

② 该程序中共有几个对象,分别属于哪个类? 为什么在程序的开头处通过语句class B; 对类B进行声明?

③ 程序中的哪两条语句会分别调用A、B的构造函数?写出这两个构造函数。

④ 函数F是什么函数?它的作用是什么?

① 36 ② n1 和 n2 两个对象,n1属于A类,n2属于B类。
因为B类的定义在A类后,而A类中要引用B类。 ③ 语句A n1(10); 和语名 B n2(8); A类的构造函数:A(int i) { a=i; } B类的构造函数:B(int i) { b=i; }

④ 函数F是友元函数。 它的作用是:提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间共享数据的机制。(关键答出共享数据、或答出通过友元可以访问到封装与类A、B中的数据)


七、编程题(11分)

定义抽象基类Shape,由它派生出三个派生类:Circle(数据成员radius)、Square(数据成员side)、和Triangle(数据成员width,high),用虚函数分别计算各种图形的面积。要求用基类指针数组, 使其每一个元素指向一个派生类的对象。在main函数中定义如下对象,并计算和输出它们的面积之和:Circle circle(12.6); Square square(3.5); Triangle triangle(4.5, 8.4) ;

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
#include <iostream>
#include <cmath>
using namespace std;

// 抽象基类 Shape
class Shape {
public:
virtual double area() const = 0; // 纯虚函数,计算面积
};

// 派生类 Circle
class Circle : public Shape {
private:
double radius; // 半径
public:
Circle(double r) : radius(r) {} // 构造函数
double area() const override { // 重写基类的虚函数
return M_PI * radius * radius; // 计算圆的面积
}
};

// 派生类 Square
class Square : public Shape {
private:
double side; // 边长
public:
Square(double s) : side(s) {} // 构造函数
double area() const override { // 重写基类的虚函数
return side * side; // 计算正方形的面积
}
};

// 派生类 Triangle
class Triangle : public Shape {
private:
double width; // 底边长
double high; // 高
public:
Triangle(double w, double h) : width(w), high(h) {} // 构造函数
double area() const override { // 重写基类的虚函数
return 0.5 * width * high; // 计算三角形的面积
}
};

int main() {
Circle circle(12.6);
Square square(3.5);
Triangle triangle(4.5, 8.4);

Shape* shapes[3]; // 基类指针数组
shapes[0] = &circle; // 圆的指针
shapes[1] = &square; // 正方形的指针
shapes[2] = &triangle; // 三角形的指针

double totalArea = 0; // 总面积
for (int i = 0; i < 3; ++i) {
totalArea += shapes[i]->area(); // 计算每个图形的面积并累加
}

cout << "Total area: " << totalArea << endl; // 输出总面积
return 0;
}

这段代码定义了一个抽象基类 Shape,以及三个派生类 CircleSquareTriangle,分别表示圆、正方形和三角形。每个类都实现了纯虚函数 area() 以计算各种图形的面积。在 main() 函数中创建了一个基类指针数组,每个指针指向一个派生类对象,然后通过循环计算并累加每个图形的面积,最后输出总面积。