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