面向对象C++编程与实践考试答案与解析

一、选择题(每题2分,共2×20=40分)

(1) 以下不能对数组 a 进行正确初始化的语句是( C )。
A. int a[2][3] = { 0 };
B. int a[ ][3] = { {0,1}, {0} };
C. int a[2][3] = { {0,1}, {2,3}, {4,5} };
D. int a[ ][3] = { 0, 1, 2, 3, 4, 5 };

【解析】

  • 选项A:二维数组可以只对部分元素赋值。 int a[2][3] = { 0 }; 运行后,数组 a 的第一个元素被赋值为 0 ,而剩下的元素可默认被赋值为 0 ;即 int a[2][3] = { 0 }; 等价于 int a[2][3] = { {0,0,0}, {0,0,0} };

  • 选项B:二维数组的一维下标可以不显式说明。即 int a[ ][3] = { {0, 1}, {0} }; 等价于 int a[2][3] = { {0, 1}, {0} }; 等价于 int a[2][3] = { {0, 1, 0}, {0, 0, 0} };

  • 选项C:int a[2][3] 为一个 2 行 3 列 的数组,而 { {0,1}, {2,3}, {4,5} }; 为 3 行 2 列,维度不匹配,初始化错误!

  • 选项D:二维数组是按行存储的。 int a[ ][3] = { 0, 1, 2, 3, 4, 5 }; 等价于 int a[2][3] = { {0, 1, 2}, {3, 4, 5} };


(2) 用 const 声明的变量( C )。
A. 不能被读取
B. 在声明时可定义也可不定义
C. 只能被读取
D. 其值在程序中可以被改变

【解析】

符号常量 const 在使用之前一定要首先声明,并且要赋初值(定义),且在程序中不能改变其值。故选项 A、 B 、D 均错误。


(3) 有语句 int a[ ] = { 0, 2, 4, 6, 8, 10 },*p = a + 1; 其值等于 0 的表达式是( D )。
A. * (p++)
B. *(++p)
C. *(p--)
D. *(--p)

【解析】

首先是数组+1的问题。在 C++ 中,一个数组 a ,其中 a 既表示数组名,同时也是数组的首地址,指向数组的第一个元素。而 a + 1 就是将 a 的值(地址)加上单个数组元素个长度(这里是一个 int 的长度),所以 a + 1 指向了第二个元素,即 a[1]。所以 *p = a + 1; 等价于 *p = & a[1]; 也就是说*p 的值等于 2 。

然后就是前置(后置)自增(自减)的问题。以自增为例,假设 i = 1;那么:

  • a = i++;相当于 a = i; i++; 此时 a = 1,i = 2;
  • a = ++i;相当于 i++; a = i; 此时 i = 2, a = 2;

最后就是指针的加减问题。选项 A ~ D 形如 *(x),则:

  • 选项 A,x = p++ 相当于 x=p; p = p+1; ⇒ *(p++) = *(p) = & a[1]; ⇒ 值等于 2
  • 选项 B,x = ++p 相当于 p = p+1; x=p; ⇒ *(++p) = *(p+1) = & a[2]; ⇒ 值等于 4
  • 选项 C,x = p-- 相当于 x=p; p = p-1; ⇒ *(p--) = *(p) = & a[1]; ⇒ 值等于 2
  • 选项 D,x = --p 相当于 p = p-1; x=p; ⇒ *(++p) = *(p-1) = & a[0]; ⇒ 值等于 0

(4) 下列的描述中( C )是错误的。
A. 使用全局变量可以从被调用函数中获取多个操作结果
B. 局部变量可以初始化,若不初始化,则系统默认它的值为 0
C. 当函数调用完后,静态局部变量的值不会消失
D. 全局变量若不初始化,则系统默认它的值为 0

【解析】

首先是关于局部变量和全局变量初始化的问题。全局变量不初始化,系统是会默认为它赋值为 0 的 ;而局部变量不初始化,系统会默认为它赋值一个随机的值。故 B 和 D 正确。

然后是局部变量和全局变量使用的问题。全局变量在的生存期是从其被定义到程序运行结束的,其间,它的值可以被任何函数调用或修改,所以 A 是正确的。静态局部变量即定义为static的局部变量,这个静态局部变量的值在函数调用完后,其所占用的内存是不会被释放的,即它在程序结束前是一直占据这个内存而不会消失的,自然其内的值也就不会消失了,而如果不是静态局部变量 ,而只是普通的变量的话,在函数调用完后,它所占据的内存将会被释放,那么这个值也就没了,即消失。所以 C 是错误的。


(5) C++ 语言的跳转语句中,对于 break 和 continue 说法正确的是( B )。
A. break 语句只能用于循环体中
B. continue 语句只能用于循环体中
C. break 是无条件跳转语句,continue 不是
D. break 和 continue 的跳转范围不够明确,容易产生问题

【解析】

break 语句可以用于 switch 或循环体内,作用是跳出 switch 或循环体。continue 语句只能用于循环体内,作用是结束当前循环,进入下次循环。因此这两种语句跳转范围很清晰,并且都是需要判断的(即需要 if 语句)。所以选 B 。


(6) S 是字符数组,用于存储字符串,下面不能够判断字符串 S 是空串的是( A )。
A. if (S[0] == 0 )
B. if (strlen(S) == 0)
C. if (strcmp(S, "") == 0)
D. if (S == '\0')

【解析】

字符串数组为空时,strlen 长度为 0,与空字符"" 比较 strcmp 返回 0 ,字符串数组里仅有 "\0" 一个元素。所以 B、C、D 全对,A 错误。


(7) C++ 函数的参数传递方式有( D )。
A. 地址传递
B. 引用传递
C. 双向值传递
D. 由用户指定传递方式

【解析】

函数的参数传递有很多种方式,具体有:

  • 值传递:例如 void func(int x, int y)
  • 地址传递(指针):例如 void func(int *x, int *y)
  • 引用传递:例如 void func(int &x, int &y)

因此传递方式可以根据实际需求由用户指定任意一种即可。故选 D 。


(8) 下面判断是否构成重载函数的条件中,错误的判断条件是( D )。
A. 参数类型不同
B. 参数个数不同
C. 参数顺序不同
D. 函数返回值不同

【解析】

所谓函数重载,就是指同样的函数名的情况下,其形参的个数、类型、顺序不同,而函数返回值不同则不属于定义范围。故选 D 。


(9) 设 a 和 b 是两个结构体变量,下面正确的表达式是( A )。
A. a = b
B. a = b + 1
C. a > b
D. a == b

【解析】

结构体变量只能进行两种运算:整体引用(赋值,参数传递)或访问成员(点运算—地址方式简化,地址方式)。其中 选项 A 即为整体引用中的赋值操作。


(10) 派生类对象只能访问基类中的( A )。
A. 公有继承的公有成员
B. 公有继承的私有成员
C. 公有继承的保护成员
D. 私有继承的公有成员

【解析】

根据类的继承关系定义:

当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。也就是说基类的公有成员和保护成员被集成到派生类中访问属性不变,仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问它们。在类族之外只能通过派生类的对象访问从基类继承的公有成员。而无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。

当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。也就是说基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类族外部通过派生类的对象无法直接访问它们。无论是派生类的成员还是派生类的对象,都无法直接访问从基类继承的私有成员。

综上,本题选 A 。


(11) 关于构造函数的叙述中,错误的说法是( B )。
A. 构造函数的名称必须为类名
B. 构造函数最多只能有一个
C. 创建对象时自动执行构造函数
D. 构造函数无任何函数类型

【解析】

根据构造函数的定义,构造函数的函数名与类名相同,并且没有返回值,也就不存在函数类型。构造函数在对象被创建的时候将被自动调用。因此 A、C、D 均对。对于选项 B,构造函数也是可以重载的,因此可以存在多个构造函数,选项 B 错误。


(12) ( A )只能访问静态成员变量。
A. 静态成员函数
B. 虚函数
C. 构造函数
D. 析构函数

【解析】

静态成员函数内部只能访问静态成员变量/函数,不能访问非静态成员变量/函数。因为非静态成员变量/函数在创建对象的时候才存在。而静态成员可以直接通过类调用,可能压根没有对象。故选 A 。


(13) C++ 中类有两种用法,一种是类的实例化,即生成类对象;另一种是通过( B )派生出新的类。
A. 复用
B. 继承
C. 重载
D. 封装

【解析】类的用法即实例化和继承。选 B 。


(14) 下面描述中,正确的是( D )。
A. 虚函数是没有实现的函数
B. 纯虚函数的实现在派生类定义
C. 抽象类是具有纯虚函数的类
D. 抽象类指针可以指向不同的派生类

【解析】

选项 A, 错误,纯虚函数才是没有实现的函数
选项 B,错误,纯虚函数在基类中不可以给出具体实现
选项 C,错误, 抽象类是有纯虚函数的类
选项 D,正确。


(15) 一个类的友函数能够访问该类的( D )。
A. 私有成员
B. 保护成员
C. 公有成员
D. 所有成员

【解析】

类的友函数与其他成员函数一样,对类内的所有成员都具有访问权限。故选 D 。


(16) 下列关于构造函数说法不正确的是( C )。
A. 构造函数必须与类同名
B. 构造函数可以省略不写
C. 构造函数必须有返回值
D. 在构造函数中可以对类中的成员进行初始化

【解析】

根据构造函数的定义,构造函数的函数名与类名相同,并且没有返回值,也就不存在函数类型。构造函数在对象被创建的时候将被自动调用。构造函数可以访问类中所有成员,因此可以承担初始化的功能。故选 C 。


(17) 关于 new 运算符的下列描述中,( D )是错误的。
A. 它可以用来动态创建对象和对象数组
B. 使用它创建对象或对象数组,可以使用 delete 删除
C. 使用它创建对象时要调用构造函数
D. 使用它调用对象数组时不许指定初始值

【解析】

建立和删除堆对象(对象或对象数组),使用 new 和 delete,二者刚好对应于构造函数和析构函数。在调用对象数组时,必须指定初始值,故 D 错误。


(18) 建立包含有类对象成员的派生类对象时,自动调用构造函数的执行顺序依次为( D )的构造函数。
A. 基类、自己所属类、成员对象成员所属类
B. 对象成员所属类、基类、自己所属类
C. 自己所属类、对象成员所属类、基类
D. 基类、对象成员所属类、自己所属类

【解析】

派生类构造函数执行的一般次序如下。

  1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)。
  2. 对派生类新增的成员对象初始化,调用顺序按照它们在类中声明的顺序。
  3. 执行派生类的构造函数体中的内容。

根据以上定义,选择 D 。


(19) 下列说法正确的是( B )。
A. 内联函数在运行时是将该函数的目标代码插入每个调用该函数的地方
B. 内联函数在编译时是将该函数的目标代码插入每个调用该函数的地方
C. 类的内联函数必须在类体内定义
D. 类的内联函数必须在类体外通过关键字 inline 定义

【解析】

内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用的地方。类的内联函数既可以在类体内定义(隐式声明,不使用 inline)也可以在类体外定义(显示声明,使用 inline),没有硬性规定。故选 B 。


(20) 执行语句:

int a = 10, b;
int &pa = a, &pb = b;

后,下列正确的语句是( B )。
A. &pb = a
B. pb = pa
C. &pb = &pa
D. *pb = *pa

【解析】

引用相当于变量的别名,对引用的操作与对变量直接操作完全一样。对变量赋值,语法合理,选项 B 正确。

引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。故 A 和 C 错误。



二、判断题(每题 1 分,共10×1 =10分)

(1) 函数的形参是局部变量。( √ )

【解析】形参只在函数内起作用,属于局部变量。


(2) 有语句 #define n 10 ,则 int a[n] ; 定义数组是合法的。( √ )

【解析】#define 的功能是将标识符定义为其后的常量。一经定义,程序中就可以直接用标识符来表示这个常量。千万不要在 #define 语句末尾加分号”;“ 否则后续的数组定义则不合法。


(3) 赋值运算符只能作为成员函数进行重载。( × )

【解析】C++规定=,[ ],(),->这四个运算符只能被重载为类的非静态成员函数。


(4) 条件编译的作用是根据不同条件进行编译不同程序段,以提高程序的兼容性。( √ )

【解析】条件编译就是让程序段在满足一定的条件后才参与到程序编译中,因此可以使得同一个源程序产生不同的目标代码,兼容性被提高。


(5) 静态数据成员必须在类外定义和初始化。( × )

【解析】在C++中,类的静态成员(static member)必须在类内声明,在类外初始化。因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。


(6) 可以对类成员变量直接赋值。( × )


(7) 在 class 定义一个类时,数据成员函数的默认访问权限是私有的。( √ )


(8) 抽象类通常应用派生类。( √ )

【解析】抽象类不能够实例化,因此通常需要派生类。


(9) 指针可以做任何类型的强制转换。( √ )


(10) 对象不能作为数组元素。( × )

【解析】数组的元素不仅可以是基本数据类型,也可以是自定义类型,类对象也是可以作为数组元素的。


三、编程题

1、编写程序,输入 100 个数值,从小到大排序后输出。(10分)

【解析】采用冒泡排序法即可。

#include <iostream>
using namespace std;
int main()
{
    int n, i, j, t, a[100];
    cin >> n;
    for (i = 0; i < n; i++)
        cin >> a[i];
    for (i = 0; i < n - 1; i++)
        for (j = 0; j < n - 1 - i; j++)
            if (a[j] > a[j + 1])
            {
                t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
            }
    for (i = 0; i < n; i++)
        cout << a[i] << " ";
    cout << endl;
    return 0;
}

2、输入一行字符,按输入字符的反序建立一个单向链表存储这些字符,并输出该链表中的字符。(10分)
节点的结构:

struct node{
    char ch;
    node *next;
};
#include <iostream>
using namespace std;
struct node
{
    char ch;
    node *next;
};
void show(node *head);
int main()
{
    node *head, *p;
    char c;
    head = NULL;
    while ((c = getchar()) != '\n') //输入一行字符
    {
        p = new node; //建立新结点
        p->ch = c;
        p->next = head; //插入表头
        head = p;
    }
    show(head);
}
void show(node *head) //输出链表
{
    node *p = head;
    cout << "链表中的字符是: \n";
    while (p)
    {
        cout << p->ch;
        p = p->next;
    }
    cout << endl;
}

3、设计三角形类 CTriangle ,写出下面 6 个成员函数。三角形类的部分定义如下:(30分)

class CTriangle{
    float a, b, c;  //三角形的三条边长
    float area;  //保存三角形的面积
    public:
    	// (1)默认构造函数
    	// (2)有初始值的构造函数
    	// (3)拷贝函数
    	// (4)设置三角形的三条边长度
    	// (5)得到三角形的三条边长度
    	// (6)“+” 运算符重载,实现 2 个三角形的面积之和
};
#include <iostream>
#include<cstring>
#include<cmath>
using namespace std;

class CTriangle {
    float a, b, c;  //三角形的三条边长
    float area;  //保存三角形的面积
public:
	
    // (1)默认构造函数
    CTriangle(){};
	
    // (2)有初始值的构造函数
    CTriangle(float x, float y, float z);
	
    // (3)拷贝函数
    CTriangle(CTriangle &t);
	
    // (4)设置三角形的三条边长度
    void Set(char point, float val);
	
    // (5)得到三角形的三条边长度
    float Get(char point);
	
    // (6)“+” 运算符重载,实现 2 个三角形的面积之和
    friend float operator+(CTriangle t1, CTriangle t2);
};

CTriangle::CTriangle(float x = 1, float y = 1, float z = 1) {
    a = x;
    b = y;
    c = z;
    float p = (a + b + c) / 2;
    area = sqrt(p * (p - a) * (p - b) * (p - c));
}

CTriangle::CTriangle(CTriangle &t) {
    a = t.a;
    b = t.b;
    c = t.c;
    area = t.area;
}

void CTriangle::Set(char point, float val) {
    if (point == 'a') {
        a = val;
    }
    else if (point == 'b') {
        b = val;
    }
    else if (point == 'c') {
        c = val;
    }else{
        cout << "设置错误!"<<endl;
    }
}

float CTriangle::Get(char point) {
    if (point == 'a') {
        return a;
    }
    else if (point == 'b'){
        return b;
    }
    else if (point == 'c') {
        return c;
    }else{
        cout << "边长指定错误!"<<endl;
        return -1;
    }
}

float operator+(CTriangle t1, CTriangle t2) {
    return t1.area + t2.area;
}


int main()
{

    // “+” 运算符重载
    CTriangle s1(3, 4, 5), s2(3, 4, 5);
    float area = s1 + s2;
    cout << "面积之和为:"<< area << endl;
    
    // 拷贝函数
    CTriangle s3 = s2;
    cout << "s3.a = "<< s3.Get('a') << endl;
	
	// 带初始值的构造函数
    CTriangle s4(5);
    cout << "s4.a = "<< s4.Get('a') << endl;
	cout << "s4.b = "<< s4.Get('b') << endl;
	
    // 取得三边长
    float a_old = s1.Get('a');
    cout << "修改前 a = " << a_old << endl;
    float a = 5;
    
    //设置三边长
    s1.Set('a', a);
    float a_new = s1.Get('a');
    cout << "修改后 a = " << a_new << endl;
	
    return 0;
}
posted @ 2021-12-29 19:06  GShang  阅读(905)  评论(0编辑  收藏  举报