全部模拟卷

《C++ 程序设计》期末模拟卷 B(偏代码计算版)

满分 100 分 建议用时 120 分钟。题型与分值清单:一、单项选择题 18 题 × 1 分 = 18 分;二、读程序写输出 7 题 × 5 分 = 35 分;三、程序填空 4 题(共约 17 空)= 17 分;四、编程大题 3 题 × 10 分 = 30 分。本卷为偏代码计算版:读程序写输出与编程大题难度上浮,重点考查指针、类、动态数据结构的代码追踪能力。请逐行模拟程序执行,注意构造/析构次序、static 记忆性、引用回写、对象切片等招牌陷阱。参考答案与解析见文末(前端默认折叠,自测时请先独立作答)。
1.

下列关于整数运算的说法中,正确的是:

A

5 / 9 * 100 的结果是 55

B

7 % 2.0 是合法表达式且结果为 1.0

C

(-7) / 2 在 C++ 中结果为 -3

D

a % ba - (a / b) * b(a、b 均为 int)结果相同

2.

已知 int a = 3;,执行 int b = a++ + ++a; 后,b 的值是:

A

6

B

7

C

8

D

9

3.

下列代码片段的输出是:

int x = 2;
switch (x) {
    case 1: cout << "A";
    case 2: cout << "B";
    case 3: cout << "C"; break;
    default: cout << "D";
}
A

B

B

BC

C

BCD

D

D

4.

int arr[5] = {10, 20, 30, 40, 50}; int *p = arr;,则 *(p + 2) + 1 的值是:

A

21

B

31

C

41

D

32

5.

关于 const 与指针,下列定义中编译错误的是:

int m = 1, n = 2;
const int *p1 = &m;     // (Ⅰ)
int * const p2 = &m;    // (Ⅱ)
A

p1 = &n;

B

*p1 = 10;

C

*p2 = 10;

D

B 和 C 都错误

6.

下列运算符中,只能重载为类的成员函数(不能重载为友元/全局函数)的是:

A

+

B

==

C

[]

D

<<

7.

执行下列声明,共调用类 Box 的构造函数(含拷贝构造)多少次?

Box a(1), b[3], *p[4], &r = a;
A

3

B

4

C

5

D

8

8.

关于静态数据成员,下列说法错误的是:

A

静态数据成员被该类的所有对象共享

B

静态数据成员必须在类外单独定义并分配空间

C

静态常量整型成员(static const int)可以在类定义中直接初始化

D

静态数据成员的存储空间随每个对象创建而单独分配

9.

DB 的公有派生类,有 B b, *pb; D d, *pd;,下列语句中不符合赋值兼容规则的是:

A

pb = &d;

B

pb = pd;

C

b = d;

D

pd = pb;

10.

关于虚函数与多态,下列说法正确的是:

A

通过对象名(而非指针或引用)调用虚函数也能实现动态绑定

B

把派生类对象赋给基类对象时,派生类特有的数据成员仍被保留

C

经基类指针调用虚函数时,实际调用的版本由指针所指对象的真实类型决定

D

构造函数可以声明为虚函数

11.

下列关于函数参数传递的说法正确的是:

A

数组作形参时按值传递整个数组的副本

B

引用形参在函数内的修改不会影响实参

C

引用形参等价于给实参起了一个别名,函数内对其修改会回写到实参

D

指针形参无法修改实参指向的内容

12.

关于 static 局部变量,下列说法错误的是:

A

它在程序运行期间始终存在,多次进入函数时保留上次的值

B

若未显式初始化则自动初始化为 0

C

它的初始化语句在每次进入函数时都会重新执行一次

D

其他函数不能直接访问它

13.

给定 char *s[] = {"red", "green", "blue"};,下列输出为 reen 的表达式是:

A

cout << s[0];

B

cout << s[1] + 1;

C

cout << *(s[1]) + 1;

D

cout << s[1][1];

14.

对于类模板,下列说法错误的是:

A

类模板本身不是一个具体类,必须实例化后才能创建对象

B

同一类模板用不同类型参数实例化得到的是互不相同的类

C

函数模板中模板参数的类型由调用时的实参类型自动推导

D

类模板的成员函数都必须在类体内部定义,不能在类体外定义

15.

关于异常处理 try/throw/catch,下列说法正确的是:

A

catch(double) 能捕获 throw 3;(int)抛出的异常

B

throw 抛出异常后,当前已经构造完成的局部对象不会被析构

C

异常类型匹配是精确匹配,不会发生像函数调用那样的标准类型提升

D

一个 try 块只能跟随一个 catch

16.

关于继承体系中的构造与析构次序,下列正确的是:

A

构造次序为:派生类 → 对象成员 → 基类

B

析构次序为:基类 → 对象成员 → 派生类

C

构造次序为:基类 → 对象成员 → 派生类,析构次序相反

D

对象成员的构造次序由初始化列表中书写的先后决定

17.

关于文件流,下列说法正确的是:

A

ofstream 以默认模式打开已存在文件时会保留原有内容并在末尾追加

B

if (!fin) 可用于判断文件是否打开失败

C

while (fin >> x) 在读到文件末尾时仍会再执行一次循环体

D

ifstream 默认以 out 模式打开

18.

关于深拷贝与三法则(Rule of Three),下列说法错误的是:

A

含有指向动态内存指针的类,若只用编译器合成的拷贝构造函数会导致两对象共享同一块内存

B

operator= 应在释放原有空间前先判断是否为自赋值

C

浅拷贝产生的两个对象析构时会对同一块内存执行两次 delete,导致未定义行为

D

只要定义了拷贝构造函数,编译器就一定不会再要求定义 operator=

19.

(构造/拷贝构造/析构调用追踪)

#include <iostream>
using namespace std;
class Node {
    int id;
    static int cnt;
public:
    Node(int i) : id(i) { cnt++; cout << "ctor " << id << " (live=" << cnt << ")\n"; }
    Node(const Node &n) : id(n.id + 100) { cnt++; cout << "copy " << id << " (live=" << cnt << ")\n"; }
    ~Node() { cnt--; cout << "dtor " << id << " (live=" << cnt << ")\n"; }
};
int Node::cnt = 0;

Node make(Node n) { return n; }

int main() {
    Node a(1);
    Node *p = new Node(2);
    make(a);
    delete p;
    return 0;
}
20.

(static 局部变量 + 值传参 vs 引用传参)

#include <iostream>
using namespace std;
void g(int x, int &y) {
    static int s = 10;
    s += x;
    x = x + s;
    y = x - y;
    cout << "x=" << x << " y=" << y << " s=" << s << endl;
}
int main() {
    int a = 3, b = 5;
    g(a, b);
    cout << "a=" << a << " b=" << b << endl;
    g(a + 1, b);
    cout << "a=" << a << " b=" << b << endl;
    return 0;
}
21.

(指针算术与字符指针数组)

#include <iostream>
using namespace std;
int main() {
    int data[] = {2, 4, 6, 8, 10};
    int *p = data + 1;
    cout << *p++ << " " << *p << " " << *(p + 2) << endl;

    char *w[] = {"sjtu", "cpp", "exam"};
    cout << w[2] + 2 << " " << *(w[1] + 1) << " " << w[0][3] << endl;
    return 0;
}
22.

(虚函数多态 vs 对象切片)

#include <iostream>
using namespace std;
class Animal {
public:
    virtual void cry() { cout << "..." << endl; }
    void name()        { cout << "Animal" << endl; }
};
class Dog : public Animal {
public:
    void cry()  { cout << "Woof" << endl; }
    void name() { cout << "Dog" << endl; }
};
int main() {
    Dog d;
    Animal *pa = &d;
    Animal  oa = d;     // 对象赋值
    pa->cry();
    pa->name();
    oa.cry();
    oa.name();
    return 0;
}
23.

(运算符重载:前置/后置 ++ 与友元 <<)

#include <iostream>
using namespace std;
class Counter {
    int v;
public:
    Counter(int x = 0) : v(x) {}
    Counter& operator++()    { v += 2; return *this; }       // 前置
    Counter  operator++(int) { Counter t = *this; v += 2; return t; } // 后置
    friend ostream& operator<<(ostream &os, const Counter &c) {
        os << c.v;
        return os;
    }
};
int main() {
    Counter c(5);
    cout << c++ << " " << c << endl;
    cout << ++c << " " << c << endl;
    return 0;
}
24.

(递归函数追踪)

#include <iostream>
using namespace std;
int f(int n) {
    if (n <= 1) return 1;
    int r = f(n - 1) + f(n - 2);
    cout << "f(" << n << ")=" << r << " ";
    return r;
}
int main() {
    cout << "result=" << f(4) << endl;
    return 0;
}
25.

(IO 流操纵符作用范围)

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    int n = 255;
    cout << setfill('0') << setw(5) << n << endl;
    cout << hex << n << " " << n << endl;
    cout << setw(6) << oct << n << "#" << endl;
    return 0;
}
26.

(自管理字符串类的深拷贝赋值,6 分,每空 1 分)

#include <iostream>
#include <cstring>
using namespace std;
class MyStr {
    char *str;
public:
    MyStr(const char *s = "") {
        str = new char[strlen(s) + 1];
        strcpy(str, s);
    }
    MyStr(const MyStr &o) {                 // 拷贝构造(深拷贝)
        str = new char[ ____(1)____ ];
        strcpy(str, o.str);
    }
    MyStr& operator=(const MyStr &o) {      // 赋值运算符(深拷贝 + 防自赋值)
        if ( ____(2)____ ) return *this;    // 自赋值检查
        ____(3)____ ;                       // 释放原有空间
        str = new char[strlen(o.str) + 1];
        strcpy(str, o.str);
        return ____(4)____ ;
    }
    ~MyStr() { ____(5)____ ; }              // 释放空间
    void print() const { cout << str << endl; }
};
int main() {
    MyStr a("hello"), b;
    b = a;
    b.print();
    return 0;
}
27.

(二分查找补全,4 分,每空 1 分)

// 在升序数组 nums[0..len-1] 中查找 target,找到返回下标,否则返回 -1
int bsearch(int *nums, int len, int target) {
    int left = 0, right = len - 1, mid;
    while ( ____(1)____ ) {
        mid = left + (right - left) / 2;
        if (nums[mid] == target)
            return mid;
        else if ( ____(2)____ )     // 中间值偏大,向左半区收缩
            ____(3)____ ;
        else
            ____(4)____ ;           // 否则向右半区收缩
    }
    return -1;
}
28.

(文件读取与统计,4 分,每空 1 分)

#include <iostream>
#include <fstream>
using namespace std;
// 打开 fname,逐个读入整数并累加求和,输出和与个数
void sumFile(const char *fname) {
    ____(1)____ fin(fname);     // 定义输入文件流并打开
    if ( ____(2)____ ) {        // 打开失败判断
        cout << "open error" << endl;
        return;
    }
    int x, sum = 0, cnt = 0;
    while ( ____(3)____ ) {     // 逐个读取整数直到文件尾
        sum += x;
        cnt++;
    }
    cout << "sum=" << sum << " cnt=" << cnt << endl;
    ____(4)____ ;               // 关闭文件
}
29.

(递归字符串反转,3 分,每空 1 分)

#include <iostream>
#include <cstring>
using namespace std;
// 用递归把字符数组 s 原地逆序,i、j 分别为待交换的左右下标
void reverse(char *s, int i, int j) {
    if ( ____(1)____ ) return;      // 递归终止条件
    char t = s[i];
    s[i] = s[j];
    s[j] = t;
    ____(2)____ ;                   // 向内递归一层
}
int main() {
    char buf[] = "abcde";
    reverse(buf, 0, ____(3)____ );  // 传入初始右边界下标
    cout << buf << endl;
    return 0;
}
30.

(类设计 + 运算符重载 + 友元,第 10 章,10 分)

设计一个复数类 Complex,要求:

  1. double re, im; 两个私有数据成员,提供带默认参数的构造函数 Complex(double r = 0, double i = 0)
  2. 重载 operator+,实现两个复数相加,返回新的 Complex 对象;
  3. 友元函数形式重载 operator<<,按 a+bi 格式输出(例如 3+4i,虚部为负时输出 3-4i);
  4. 友元函数形式实现 double modulus(const Complex &c),返回复数的模 re2+im2\sqrt{re^2 + im^2}

请写出完整类定义与各函数实现,并给出一个能演示上述功能的 main 函数。

31.

(抽象类与多态层次,第 12 章,10 分)

设计一个图形继承体系:

  1. 抽象基类 Shape,含一个纯虚函数 double area() const;,并提供普通成员函数 void show() const;,在其中调用 area() 输出该图形的面积;
  2. 派生类 Circle(数据成员半径 r,面积 πr2\pi r^2)与 Rectangle(数据成员宽 w、高 h,面积 w×hw \times h),各自重写 area()
  3. π\pi 用类内共享常量表示(如 static const double PI;);
  4. main 中建立一个 Shape* 指针数组,分别指向 CircleRectangle 对象,通过遍历该数组以多态方式调用 show() 输出各图形面积。

请写出完整代码。

32.

(动态内存数据结构类,第 7 章 + 第 10 章,10 分)

设计一个动态整型数组类 IntVector,要求实现完整的「三法则」(Rule of Three):

  1. 私有数据成员:int *data;(指向动态分配的数组)与 int size;
  2. 构造函数 IntVector(int n):动态分配 nint 并全部初始化为 0;
  3. 拷贝构造函数:实现深拷贝(不能与源对象共享同一块内存);
  4. 重载 operator=:实现深拷贝赋值,并正确处理自赋值与原有空间释放;
  5. 重载 operator[],使其可作为左值访问元素(返回引用);
  6. 析构函数:正确释放动态内存;
  7. 提供成员函数 void print() const; 顺序输出全部元素。

请写出完整类定义与各成员函数实现。