堂前测试重做:C++98 双平行卷(A/B)+ 三级难度梯度

原版 10 题单卷难度近似、缺梯度、无平行卷,区分度不足。新版改为两份等价
平行卷(防抄袭 / 可重测),按 L1 初级 / L2 中级 / L3 进阶三级分布,补齐
对象切片、菱形继承二义、const 与指针等真正能筛出高阶学员的考点;严格限定
C++98,不涉及现代特性。所有代码题输出经 g++ -std=c++98 -pedantic-errors
实测核实,修正了 B 卷第 2 题链式 << 求值顺序未定义的缺陷(改为三行输出)。
This commit is contained in:
张宗平
2026-07-04 13:31:33 +08:00
parent 9765865d13
commit 6640baffae
+323 -86
View File
@@ -1,56 +1,108 @@
# C++ 基础摸底小测验(10 题,预计 10 分钟) # C++ 基础摸底小测验(10 分钟 / 10 题 · 两份等价平行卷
> 用途:验证 `docs/teaching/OUTLINE.md` §0 里"学员已掌握 C++ 基础语法与 OOP > **用途**:验证 [`OUTLINE.md`](OUTLINE.md) §0 里"学员已掌握 C++ 基础语法与 OOP 概念"这条假设
> 概念"这条假设是否成立。建议在 **Day1 模块 B 开始前**(C++ 回顾环节之前) > 是否成立。建议在 **Day1 模块 B 开始前**(C++ 回顾环节之前)发给学员,闭卷、不查资料,
> 发给学员,闭卷、不查资料,10 分钟内独立完成。 > **10 分钟内独立完成**
> >
> 题型:选择题 + 代码阅读(判断输出/找 bug)+ 1 道简答,覆盖 Day1 回顾要点 > **本卷语言基线 = C++ 98**:刻意不涉及任何 C++11/14/17/20+ 现代特性(auto / 智能指针 /
> 涉及的引用、重载、构造析构顺序、虚函数多态、运算符重载、模板、STL、 > 移动语义 / lambda / override / 列表初始化等)。目的是先把"经典 C++ / OOP 这块根基本身
> const、深浅拷贝九个知识点 > 是否扎实"测清楚——这是 Qt 课程 C++ 部分的真正地基;现代特性的回顾放在测验之后、随堂进行
>
> **两份平行卷(A / B)**:覆盖**相同的知识点梯度、相同的难度分布**,题目平行但不雷同,
> 用于以下场景:
> - **防抄袭**:相邻座席发不同卷(A/B 交错);
> - **重测**:对摸底结果存疑的学员,换卷再测一次,两次难度可比;
> - **平行对照**:A 卷统一当堂测、B 卷留作补测/作业,便于横向比较。
>
> **三级难度分布(每份卷 10 题)**:
>
> | 层级 | 题号 | 能力定位 | 答错意味着 |
> |---|---|---|---|
> | **L1 初级**(3 题)| 1–3 | 记得住、读得懂基础语法 | 根基明显不足,Day1 需回退到语法讲解 |
> | **L2 中级**4 题)| 4–7 | OOP 三大特性、内存模型有概念 | 概念有但没吃透,讲课时在对应点加时 |
> | **L3 进阶**3 题)| 8–10 | 多态细节、继承陷阱、能分析真题 | 达到"高阶"假设,可全速推进 |
>
> 题型:**选择题 + 代码阅读(判断输出/找 bug)**。每题标注建议用时,加起来约 10 分钟。
--- ---
## 第 1 题(选择,30 秒) # A 卷
下列关于"引用"和"指针"的说法,**错误**的是? ## 【L1 · 第 1 题】选择(30 秒)· 引用基础
A. 引用必须在定义时初始化 下列关于"引用(reference"和"指针(pointer"的说法,**错误**的是?
B. 引用可以为 `nullptr`
C. 引用一旦绑定就不能再指向其他对象
D. 函数参数用引用可以避免拷贝开销
## 第 2 题(代码阅读,1 分钟) - A. 引用必须在定义时初始化
- B. 引用可以绑定到 `nullptr`
- C. 引用一旦绑定到一个对象,就不能再改绑到另一个对象
- D. 函数参数用引用传递,可以避免实参被拷贝
## 【L1 · 第 2 题】代码阅读(45 秒)· static 局部变量
```cpp ```cpp
void f(int x) { std::cout << "int " << x; } #include <iostream>
void f(double x) { std::cout << "double " << x; } int counter() {
static int n = 0; // 只在第一次进入时初始化一次
int main() { f(3.0f); } return ++n;
}
int main() {
std::cout << counter() << counter() << counter();
}
``` ```
这段代码输出什么? 请写出程序的输出。
## 第 3 题(选择,30 秒) ## 【L1 · 第 3 题】代码阅读(45 秒)· 值传递 vs 引用传递
```cpp
#include <iostream>
void f(int x) { x = x + 1; } // 形参 x 是实参的副本
int main() {
int n = 10;
f(n);
std::cout << n;
}
```
请写出程序的输出。
## 【L2 · 第 4 题】代码阅读(1 分钟)· 函数重载决议
```cpp
#include <iostream>
void h(long) { std::cout << "L"; }
void h(double) { std::cout << "D"; }
int main() { h(5); }
```
这段代码会发生什么?(编译通过则写出输出;不能编译则说明原因)
## 【L2 · 第 5 题】选择(45 秒)· 虚函数与多态
关于虚函数,下列说法**正确**的是? 关于虚函数,下列说法**正确**的是?
A. 基类析构函数不加 `virtual`,通过基类指针 `delete` 派生类对象一定不会有问题 - A. 只要基类析构函数不加 `virtual`,通过基类指针 `delete` 派生类对象一定有问题
B. 构造函数可以虚函数 - B. 构造函数可以声明为虚函数
C. 纯虚函数所在的类不能被直接实例化 - C. 含有纯虚函数`= 0`)的抽象类不能被直接实例化
D. 派生类必须重写基类每一个虚函数 - D. 派生类必须重写基类中的**每一个**虚函数,否则编译报错
## 第 4代码阅读1 分钟) ## 【L2 ·6代码阅读1 分钟)· 构造与析构顺序
```cpp ```cpp
class A { public: A(){ std::cout<<"A"; } ~A(){ std::cout<<"~A"; } }; #include <iostream>
class B : public A { public: B(){ std::cout<<"B"; } ~B(){ std::cout<<"~B"; } }; struct M { M(){ std::cout<<"M"; } ~M(){ std::cout<<"~M"; } };
struct Base { Base(){ std::cout<<"B"; } ~Base(){ std::cout<<"~B"; } };
int main(){ B b; } struct Derived : Base {
M m; // 成员对象
Derived(){ std::cout<<"D"; }
~Derived(){ std::cout<<"~D"; }
};
int main(){ Derived d; }
``` ```
请写出输出的完整顺序。 请写出构造与析构的完整输出顺序。
## 第 5找 bug1.5 分钟) ## 【L2 ·7找 bug1.5 分钟)· 浅拷贝与 double free
```cpp ```cpp
class Buffer { class Buffer {
@@ -63,94 +115,279 @@ public:
void use() { void use() {
Buffer a(10); Buffer a(10);
Buffer b = a; // 用 a 拷贝构造 b Buffer b = a; // 用 a 拷贝构造 b
} // a、b 离开作用域
```
`use()` 运行到结束时会发生什么问题?请说明原因。
## 【L3 · 第 8 题】代码阅读(1 分钟)· 对象切片
```cpp
#include <iostream>
struct Base {
virtual const char* name() const { return "Base"; }
};
struct Derived : Base {
const char* name() const { return "Derived"; } // 重写虚函数
};
void print(Base b) { // 注意:按值传递,不是引用/指针
std::cout << b.name();
}
int main() {
Derived d;
print(d);
} }
``` ```
`use()` 函数运行到结束时会发生什么问题?请说明原因 请写出程序的输出,并解释为什么不是 `Derived`
## 第 6选择30 秒) ## 【L3 ·9选择1 分钟)· const 与指针
关于函数模板,下列说法**正确**的是? 下列声明中,表示"**指针本身是常量**(不能再指向别的地址),但指针指向的数据可以修改"的是?
A. 模板函数一旦定义,编译器就会为所有可能的类型生成代码 - A. `const char* p`
B. 编译器会根据调用时的实参类型自动推导模板参数,并生成对应的实例 - B. `char const* p`
C. 模板函数不能被重载 - C. `char* const p`
D. 调用模板函数时必须显式指定所有模板参数 - D. `const char* const p`
## 第 7代码阅读1 分钟) ## 【L3 · 第 10代码阅读1.5 分钟)· 多重继承的二义性
```cpp ```cpp
std::vector<int> v = {3, 1, 4, 1, 5}; class Base { public: int value; };
for (int x : v) { class Left : public Base {}; // 普通继承
if (x % 2 == 0) std::cout << x; class Right : public Base {}; // 普通继承
class Derived : public Left, public Right {};
int main() {
Derived d;
d.value = 10; //
} }
``` ```
这段代码输出什么? `d.value = 10;` 这一行能编译通过吗?如果可以,`value` 来自哪条继承链路?如果不能,请说明原因。
## 第 8 题(选择,30 秒) ---
---
下列哪个函数声明表示"这个成员函数不会修改对象的成员变量"? # B 卷
A. `void print()` > 与 A 卷一一对应、考点相同、难度相当的平行卷。
B. `void print() const`
C. `const void print()`
D. `void const print()`
## 第 9(代码阅读,1 分钟) ## 【L1 ·1】选择(30 秒)· 指针基础
下列关于"指针"的说法,**错误**的是?
- A. 指针可以不初始化(此时其值未定义)
- B. 指针可以在生命周期内先后指向不同的对象
- C. 指针变量本身在内存中也占用存储空间
- D. 用 `delete` 释放一个指针后,该指针自动变成空指针 `nullptr`
## 【L1 · 第 2 题】代码阅读(45 秒)· static 局部变量
```cpp ```cpp
class Point { #include <iostream>
int tick() {
static int n = 100; // 只在第一次进入时初始化一次
return n--;
}
int main() {
std::cout << tick() << "\n";
std::cout << tick() << "\n";
std::cout << tick() << "\n";
}
```
请写出程序的输出(共三行)。
## 【L1 · 第 3 题】代码阅读(45 秒)· 值传递 vs 引用传递
```cpp
#include <iostream>
void f(int& x) { x = x + 1; } // 形参 x 是实参的别名
int main() {
int n = 10;
f(n);
std::cout << n;
}
```
请写出程序的输出。
## 【L2 · 第 4 题】代码阅读(1 分钟)· 函数重载决议
```cpp
#include <iostream>
void h(short) { std::cout << "S"; }
void h(float) { std::cout << "F"; }
int main() { h(7); }
```
这段代码会发生什么?(编译通过则写出输出;不能编译则说明原因)
## 【L2 · 第 5 题】选择(45 秒)· 虚函数与多态
关于虚函数,下列说法**正确**的是?
- A. 基类把析构函数声明为 `virtual`,是为保证 `delete 基类指针` 时派生类部分也能被正确析构
- B. 在构造函数里调用虚函数,会触发派生类的虚函数版本
- C. 派生类重写基类虚函数时,可以任意改变返回类型,不受限制
- D. 一个类只要有虚函数,编译器就**不会**为它生成虚函数表(vtable)
## 【L2 · 第 6 题】代码阅读(1 分钟)· 构造与析构顺序
```cpp
#include <iostream>
struct N { N(){ std::cout<<"1"; } ~N(){ std::cout<<"4"; } };
struct A { A(){ std::cout<<"2"; } ~A(){ std::cout<<"3"; } };
struct C : A {
N n; // 成员对象
C(){ std::cout<<"5"; }
~C(){ std::cout<<"6"; }
};
int main(){ C c; }
```
请写出构造与析构的完整输出顺序。
## 【L2 · 第 7 题】找 bug1.5 分钟)· 浅拷贝与 double free
```cpp
class Stack {
int* data;
int size;
public: public:
int x, y; Stack(int n) : size(n) { data = new int[n]; }
Point operator+(const Point& other) const { ~Stack() { delete[] data; }
return Point{x + other.x, y + other.y};
}
}; };
Point p1{1, 2}, p2{3, 4}; void use() {
Point p3 = p1 + p2; Stack a(10);
std::cout << p3.x << "," << p3.y; Stack b = a; // 用 a 拷贝构造 b
} // a、b 离开作用域
``` ```
这段代码输出什么? `use()` 运行到结束时会发生什么问题?请说明原因。
## 第 10 题(简答,1.5 分钟) ## 【L3 · 第 8 题】代码阅读(1 分钟)· 对象切片
用一两句话说明"深拷贝"和"浅拷贝"的区别,以及为什么一个类里如果有指针成员、 ```cpp
又没有自己实现拷贝构造函数,可能会引发问题。 #include <iostream>
struct Animal {
virtual const char* sound() const { return "..."; }
};
struct Dog : Animal {
const char* sound() const { return "Woof"; } // 重写虚函数
};
const char* record(Animal a) { // 注意:按值传递,不是引用/指针
return a.sound();
}
int main() {
Dog d;
std::cout << record(d);
}
```
请写出程序的输出,并解释为什么不是 `Woof`
## 【L3 · 第 9 题】选择(1 分钟)· const 与指针
下列声明中,表示"**指针指向的数据是常量**(不能通过该指针改数据),但指针本身可以指向别的地址"的是?(本题有两个等价正确选项,全选才得分)
- A. `const char* p`
- B. `char const* p`
- C. `char* const p`
- D. `const char* const p`
## 【L3 · 第 10 题】代码阅读(1.5 分钟)· 多重继承的二义性
```cpp
class Root { public: int id; };
class A : public Root {}; // 普通继承
class B : public Root {}; // 普通继承
class Final : public A, public B {};
int main() {
Final f;
f.id = 5; //
}
```
`f.id = 5;` 这一行能编译通过吗?如果可以,`id` 来自哪条继承链路?如果不能,请说明原因。
---
--- ---
## 答案与评分(教师用,建议测验时不发给学员) ## 答案与评分(教师用,建议测验时不发给学员)
| 题号 | 答案 | 对应知识点 · 出处 | > 所有"代码阅读 / 找 bug"题的输出与编译行为,均已用 **g++ 13.3 在 `-std=c++98
> -pedantic-errors -Wall` 严格模式下编译运行核实**,非凭记忆作答。
### A 卷答案
| 题号 | 答案 | 考查点 · 课程出处 |
|---|---|---| |---|---|---|
| 1 | B(引用不能为空,这是它和指针的关键区别之一) | 引用的本质 · `p01/ch03/reference_essence.cpp` | | L1·1 | **B**(引用不能为空,这是它和指针的关键区别之一——引用是已绑定对象的别名,不存在"空引用" | 引用的本质 · `p01/ch03/reference_essence.cpp` |
| 2 | `double 3``float``double` 是浮点提升,比 `float``int` 的转换更优先匹配 `f(double)` | 函数重载决议 · `p01/ch03/function_overload_mechanism.cpp` | | L1·2 | `123``static` 局部变量只初始化一次,之后跨调用保持值;三次调用依次返回 1、2、3) | static 与存储期 · `p01/ch04/s04` |
| 3 | C(A 错——没有 virtual 析构会导致派生类部分不被析构;B 错——构造函数不能是虚函数;D 错——不重写就继承基类实现,纯虚函数才必须重写 | 函数与抽象类 · `p01/ch04/s08` | | L1·3 | `10`(值传递:`f` 内改的是 `n` 的副本,函数返回后副本销毁,`n` 不变 | 函数参数传递 · `p01/ch03` |
| 4 | `A B ~B ~A`(先构造基类再构造派生类;析构顺序相反) | 继承中构造/析构顺序 · `p01/ch04/s07/inheritance_ctor_dtor_order.cpp` | | L2·4 | **编译失败**`h(5)``int` 既要转 `long` 又要转 `double`,两个都是"标准转换",优先级相同 → 重载二义(`ambiguous`)。必须显式写 `h(5L)``h(5.0)` 消歧义 | 函数重载决议 · `p01/ch03/function_overload_mechanism.cpp` |
| 5 | 浅拷贝:`b``data``a``data` 指向同一块内存;函数结束时 `~Buffer()` 被调用两次,对同一块内存 `delete[]` 两次(double free),行为未定义/可能崩溃 | 深拷贝 vs 浅拷贝 · `p01/ch04/s03/deep_vs_shallow_copy.cpp` | | L2·5 | **C**(A 错:非虚析构时派生类部分不会被析构,是经典内存泄漏;B 错:构造函数不能是虚函数;D 错:不重写就继承基类实现,只有纯虚函数才"必须"重写) | 虚函数与抽象类 · `p01/ch04/s08/virtual_dtor_purpose.cpp` |
| 6 | B(模板是"按需实例化",不是提前生成所有版本) | 函数模板 · `p01/ch05/function_template.cpp` | | L2·6 | `BMD~D~M~B`(构造:基类 `B` → 成员 `M` → 派生类体 `D`;析构完全相反:派生类 `~D` → 成员 `~M` → 基类 `~B` | 继承+成员的构造析构顺序 · `p01/ch04/s07/inheritance_ctor_dtor_order.cpp` |
| 7 | `4`3,1,4,1,5 里只有 4 是偶数) | STL 容器与范围 for · `p01/ch09/s03/vector_iterator.cpp` | | L2·7 | **浅拷贝导致 double free**:编译器合成的默认拷贝构造是浅拷贝,`b.data``a.data` 指向**同一块** `new[]` 内存;`use()` 结束时 `~Buffer()` 被调用两次,对同一块内存 `delete[]` 两次 → 行为未定义,通常崩溃。修复:自定义拷贝构造(深拷贝),或禁用拷贝 | 深拷贝 vs 浅拷贝 · `p01/ch04/s03/deep_vs_shallow_copy.cpp` |
| 8 | B`const` 放在参数列表后面,修饰的是"this 指向的对象不可修改" | const 成员函数 · `p01/ch04/s04/const_member_function.cpp` | | L3·8 | 输出 `Base``print(Base b)` 形参**按值接收**`Derived` 对象被**切片(slicing)**:只拷贝了 `Base` 子对象,派生类新增部分被截断,`b` 是个货真价实的 `Base`,虚函数自然调到 `Base::name`。要让多态生效,形参须为 `Base&`/`Base*` | 对象切片 · `p01/ch04/s08` |
| 9 | `4,6``1+3=4``2+4=6` | 运算符重载 · `p01/ch04/s06/overload_assignment.cpp` | | L3·9 | **C**`char* const p` —— `const` 修饰 `p` 本身,故指针不可改指向;`*p` 未被 const 修饰,数据可改。A/B 是"指向 const 的指针"D 两边都 const | const 与指针 · `p01/ch03` |
| 10 | 浅拷贝只复制指针本身(地址),深拷贝复制指针指向的内容;没有自定义拷贝构造函数时,编译器生成的默认拷贝构造函数是浅拷贝,两个对象的指针成员会指向同一块内存,析构时重复释放(double free)或修改一个对象的数据影响另一个对象 | 深拷贝 vs 浅拷贝 · `p01/ch04/s03/deep_vs_shallow_copy.cpp` | | L3·10 | **编译失败**`d.value` 二义(`ambiguous`)。`Derived``Left``Right` 两条路径各继承了一份 `Base::value`,编译器无法判断要访问哪一个。修复:①用 `d.Left::value` 显式限定;②改用**虚继承** `class Left : virtual public Base`,让两条路径共享同一个 `Base` 子对象 | 多重继承二义性与虚继承 · `p01/ch04/s07` |
### 评分建议 ### B 卷答案
- **810 题正确**:具备 `OUTLINE.md` §0 假设的基础,按 Day1 模块 B 原定的 | 题号 | 答案 | 考查点 · 课程出处 |
"巡礼式回顾"节奏推进即可。 |---|---|---|
- **5–7 题正确**:部分薄弱点——对照上表找出学员集中出错的知识点(比如普遍 | L1·1 | **D**`delete` 只释放指针指向的内存,**不改动指针本身**——它仍保存原地址,成为"悬空指针(dangling)";继续解引用是未定义行为。这是 C++98 没有 `nullptr` 时代的经典坑) | 指针与内存管理 · `p01/ch04/s03` |
在第 4/5 题失分,说明构造析构顺序和深浅拷贝没吃透),讲课时在对应知识点 | L1·2 | 三行依次输出 **`100` / `99` / `98`**`static` 局部变量只初始化一次、跨调用保持值;`n--` 先返回当前值再自减)。三行分开打印,无求值顺序歧义 | static 与存储期 · `p01/ch04/s04` |
多花 5–10 分钟,其余部分维持原节奏。 | L1·3 | `11`(引用传递:`x``n` 的别名,`x=x+1` 直接改 `n` | 函数参数传递 · `p01/ch03` |
- **0–4 题正确**:明显达不到"已掌握 C++ 基础语法与 OOP 概念"的假设——按 | L2·4 | **编译失败**`h(7)``int` 既要转 `short` 又要转 `float`,两个都是标准转换、优先级相同 → 重载二义(`ambiguous`)。需写 `h((short)7)``h(7.0f)` 消歧义 | 函数重载决议 · `p01/ch03/function_overload_mechanism.cpp` |
`OUTLINE.md` §0 里的预案,Day1 模块 B 的讲授部分退回到语法讲解,练习任务 | L2·5 | **A**(A 正确,这正是虚析构存在的意义;B 错:构造期间虚表尚未就绪/对象类型还是基类,调用基类版本;C 错:返回类型必须协变或相同,不能"任意";D 错:有虚函数才会生成 vtable) | 虚函数与抽象类 · `p01/ch04/s08/virtual_dtor_purpose.cpp` |
保持不变但预期完成度应下调,不需要重排整个教案结构。 | L2·6 | `215643`(构造:基类 `2` → 成员 `1` → 派生类 `5`;析构相反:`6``4``3` | 继承+成员的构造析构顺序 · `p01/ch04/s07/inheritance_ctor_dtor_order.cpp` |
| L2·7 | **浅拷贝导致 double free**:默认拷贝构造浅拷贝,`b.data``a.data` 同指一块 `new[]``use()` 结束时析构两次 → double free / 未定义行为。修复同 A 卷第 7 题 | 深拷贝 vs 浅拷贝 · `p01/ch04/s03/deep_vs_shallow_copy.cpp` |
| L3·8 | 输出 `...``record(Animal a)` 按值接收 → **对象切片**`Dog` 部分被截断,`a` 是个 `Animal`,调到 `Animal::sound`。要听到 `Woof`,形参须为 `Animal&`/`Animal*` | 对象切片 · `p01/ch04/s08` |
| L3·9 | **A 和 B**`const char* p``char const* p` 写法不同但完全等价——`const` 修饰的是 `*` 的左边,即指向的数据;指针本身可改。C 是 const 指针,D 两边都 const。**全选 A、B 才得分** | const 与指针 · `p01/ch03` |
| L3·10 | **编译失败**`f.id` 二义(`ambiguous`)。`Final``A``B` 两条路径各继承一份 `Root::id`,编译器无法抉择。修复:①`f.A::id` 显式限定;②虚继承 `class A : virtual public Root` | 多重继承二义性与虚继承 · `p01/ch04/s07` |
> **关于 B 卷第 2 题的设计说明**:原稿曾写成单行链式 `std::cout << tick() << tick() << tick();`
> 但该写法的**输出顺序依赖 `<<` 的求值顺序,而 C++ 未规定该顺序**unspecified behavior),
> 不同编译器可能拼出 `1009998`、`9899100` 等不同结果——会让"标准答案"本身不稳定。现稿改为
> 三行分开打印,输出确定无歧义,更适合做闭卷摸底的客观题。
### 评分与分层预案
**计分规则**:每题 1 分,满分 10 分。选择/代码阅读答对即得分;"找 bug / 解释"题(A·7、A·8、A·10、
B·7、B·8、B·10)只要**说对核心机制**(浅拷贝/double free、对象切片、二义性)即得分,不苛求
措辞。B 卷第 2 题按上述概念核分说明处理。
**分层与教学预案**(对应 `OUTLINE.md` §0):
| 分段 | 占比解读 | 指导动作 |
|---|---|---|
| **810 分** | 达到"已掌握 C++ 基础与 OOP 概念"假设 | Day1 模块 B 按原"巡礼式"节奏推进,现代特性(C++11+)正常引入 |
| **57 分** | 部分薄弱——对照上表找出**集中失分的层级** | **L1 失分多**(Q1–3):回退到基础语法讲解;**L2 失分多**(Q4–7):在构造析构/浅拷贝/重载决议处加时 5–10 分钟;**L3 失分多**(Q8–10):说明学员 OOP 基础尚可,多态/继承陷阱课上点到为止、留自学 |
| **04 分** | 明显达不到假设 | 按 `OUTLINE.md` §0 预案,Day1 模块 B **整体退回语法讲解**,练习任务保留但预期完成度下调 |
**逐题正确率用法**(大规模班级):统计每题正确率,**低于 50% 的题**对应的知识点在 Day1
模块 B 里适当加时;无需逐份细看。
### 与旧版的差异(为什么重做)
此前一版 PRETEST(10 题、单卷)的主要不足:
1. **缺梯度**——10 题难度近似,集中在"概念识别"层面,区分不出"学过"和"真正懂了"
2. **无平行卷**——单卷无法防抄袭、无法平行重测;
3. **进阶层薄弱**——没有对象切片、菱形继承这类真正能筛出"高阶"学员的题,全做对与做对 7 题
的学员水平差异,旧卷测不出来。
新版改进:**三级梯度(L1/L2/L3)+ 两份等价平行卷(A/B)+ 每题标注考查点与课程出处**,
既能给出每层级单独的掌握情况,又支持防抄袭与平行重测。
## 使用方式 ## 使用方式
- 建议纸质或线上表单均可,闭卷、不查资料、不讨论,10 分钟收卷; - 纸质或线上表单均可,闭卷、不查资料、不讨论,**10 分钟收卷**
- 只用于摸底分层,不计入 `docs/TRAINING_PLAN_2026.md` 的正式考核成绩 - 两份平行卷按需使用:相邻座席 A/B 交错防抄袭;或 A 卷当堂测、B 卷补测/作业
- 大规模学员时可以只统计"每题正确率"而不用逐份细看,正确率低于 50% 的题 - 只用于摸底分层,**不计入** `docs/TRAINING_PLAN_2026.md` 的正式考核成绩;
对应知识点在 Day1 模块 B 里适当加时。 - 大规模班级可只统计"每题正确率",正确率低于 50% 的题对应知识点在 Day1 模块 B 里适当加时。