简介:这是一份面向C++初学者与备考学生的填空题专项练习文档,聚焦面向对象程序设计的核心概念与语法细节,帮助读者通过填空形式巩固类与对象、继承、多态、模板、运算符重载、I/O流等高频考点。资源包内仅含1个doc文件,大小约59KB,内容以题目与答案对照的形式编排,覆盖构造函数与析构函数调用次数、访问控制属性、虚函数与抽象类、函数重载与模板参数、动态内存分配与释放等典型知识点,并配有标准答案便于自测与查漏补缺。文档题目数量充足、覆盖面广,适合课堂同步练习、期末复习或面试前快速回顾。目前已有47人学习下载,可作为C++语法与面向对象基础的系统性自测材料,帮助读者在填空训练中定位知识盲区,提升对关键概念的准确理解与记忆。
1. 从一份 C++ 填空题集说起:为什么“补全代码”比“从头写”更难教
带过几轮 C++ 入门的人都会发现一个反直觉现象:让新手从零写一个for循环,他可能磕磕绊绊能写出来;但给他一段挖掉两行的代码让他补全,他反而卡得更死。原因不复杂——从零写是自由发挥,补全代码却要求他同时读懂上下文、推断作者意图、还要保证语法和语义都对得上。一份 C++ 填空题集(推荐文档).doc 这类材料,本质上就是把“读代码 + 补逻辑”这个高频训练场景固化下来,用最少的篇幅逼学习者暴露知识盲区。它适合三类人:刚学完语法想检验掌握程度的初学者、准备面试想快速过一遍易错点的求职者、以及需要给学生出练习题的助教或培训讲师。这篇笔记不讲空泛的学习方法,而是把“怎么自己造一套可复现、可批改、可扩展的 C++ 填空题集”拆开讲清楚——从题目设计、答案校验到自动化判分,每一步都落到能跑的命令和代码上。
2. 填空题集的题目设计:从知识点到可判分的空
2.1 先定知识点覆盖矩阵,再动手挖空
很多人做填空题集是“看到哪段代码顺眼就挖哪”,结果做完整套发现全集中在循环和数组,指针、引用、类构造、模板一个没覆盖。我的做法是先拉一张知识点矩阵,按“语法基础 / 内存与指针 / 面向对象 / STL 容器 / 模板与泛型 / 异常与 RAII”六个维度各出若干题,保证覆盖面。矩阵不需要多复杂,一张表就够:
| 维度 | 必考知识点 | 建议题量 | 挖空类型 |
|---|---|---|---|
| 语法基础 | 类型推导、运算符优先级、作用域 | 4 | 表达式补全 |
| 内存与指针 | 指针运算、动态分配、悬垂指针 | 4 | 语句补全 |
| 面向对象 | 构造/析构、虚函数、访问控制 | 4 | 函数体补全 |
| STL 容器 | vector/map 遍历、迭代器失效 | 3 | 循环条件补全 |
| 模板与泛型 | 模板参数、特化、SFINAE 入门 | 3 | 声明补全 |
| 异常与 RAII | try/catch、智能指针、析构顺序 | 2 | 资源释放补全 |
这张表的作用是防止偏科。每道题在表里登记它属于哪个维度、考哪个知识点、挖的是哪一类空。挖空类型很关键:表达式补全和函数体补全的难度差一个量级,前者考语法敏感度,后者考逻辑组织能力。新手建议从表达式补全起步,熟手直接上函数体补全。
2.2 挖空位置决定题目质量:三个不能挖的地方
挖空不是随便删几行。血泪经验是,以下三个位置挖了会让题目变成“猜谜”而不是“填空”:
第一,不要挖掉唯一的上下文线索。比如一段代码里int* p = new int[10];后面挖掉delete[] p;,这是合理的;但如果你把new int[10]也挖掉,学习者就无从判断该用delete还是delete[],题目失去可解性。
第二,不要挖掉与题目考点无关的样板代码。#include <iostream>、using namespace std;、int main()这些属于环境代码,挖掉只会增加噪音,不增加训练价值。
第三,不要在同一道题里挖超过三个空,且空与空之间要有逻辑递进。超过三个空,学习者注意力会碎片化,变成“填字游戏”。我一般一道题挖 1 到 2 个空,最多 3 个,且第一个空是“入门级”,第二个空是“进阶级”,形成难度梯度。
2.3 用注释标注考点,但别把答案写进注释
每道题在代码上方用注释标注考点,方便批改和检索。但注意:注释里只写“考什么”,不写“怎么填”。比如:
// [考点] 动态数组释放 / delete 与 delete[] 的区别 // [难度] 入门 int* arr = new int[5]; for (int i = 0; i < 5; ++i) arr[i] = i * i; // ______ 此处补全释放语句这种注释对出题人和批改人都有用,但不会泄露答案。如果你把// 答案:delete[] arr;写进去,那这套题就废了——学习者会直接看注释而不是思考。
3. 把填空题集做成可自动判分的练习系统
3.1 题目存储格式:用结构化文本而不是纯 doc
一份 C++ 填空题集(推荐文档).doc 如果只是 Word 文档,批改全靠人工,题量一上来就崩。我的做法是把题目抽成结构化文本,每道题一个块,用固定分隔符隔开,方便脚本解析。格式如下:
=== QUESTION 001 === TOPIC: 内存与指针 DIFFICULTY: 入门 CODE: int* arr = new int[5]; for (int i = 0; i < 5; ++i) arr[i] = i * i; // ______ 此处补全释放语句 ANSWER: delete[] arr; EXPLAIN: 用 new[] 分配的数组必须用 delete[] 释放,否则行为未定义。 === END ===这个格式的好处是:CODE段可以直接渲染给学习者,ANSWER段用于自动比对,EXPLAIN段用于批改后展示解析。解析脚本用 Python 写,几十行就能跑:
import re def parse_questions(text): pattern = re.compile( r'=== QUESTION (\d+) ===\n' r'TOPIC: (.+)\n' r'DIFFICULTY: (.+)\n' r'CODE:\n(.*?)\n' r'ANSWER: (.+)\n' r'EXPLAIN: (.+?)\n' r'=== END ===', re.DOTALL ) questions = [] for m in pattern.finditer(text): questions.append({ 'id': m.group(1), 'topic': m.group(2).strip(), 'difficulty': m.group(3).strip(), 'code': m.group(4).strip(), 'answer': m.group(5).strip(), 'explain': m.group(6).strip(), }) return questions if __name__ == '__main__': with open('questions.txt', 'r', encoding='utf-8') as f: data = f.read() qs = parse_questions(data) print(f'解析到 {len(qs)} 道题') for q in qs[:2]: print(q['id'], q['topic'], q['answer'])这段代码的关键在正则的re.DOTALL标志,它让.能匹配换行符,否则CODE段的多行内容会截断。ANSWER和EXPLAIN用非贪婪匹配.+?,避免跨题吞掉下一题的内容。跑通后你会看到解析出的题目数量和前两题的答案,确认格式没问题再往下做。
3.2 答案比对:精确匹配不够,要做归一化
自动判分最容易翻车的地方是答案比对。学习者写delete [] arr;和标准答案delete[] arr;中间多一个空格,精确匹配就判错。所以比对前要做归一化:去掉首尾空白、合并连续空格、统一分号前后的空格。更稳妥的做法是同时支持“精确匹配”和“正则匹配”两种模式,标准答案里用\s*表示可选空白:
import re def normalize(s): s = s.strip() s = re.sub(r'\s+', ' ', s) s = re.sub(r'\s*([;{}()\[\],])\s*', r'\1', s) return s def check_answer(user_input, standard_answer): # 先尝试精确归一化比对 if normalize(user_input) == normalize(standard_answer): return True # 再尝试正则比对,标准答案里可写 \s* 等模式 try: if re.fullmatch(standard_answer, user_input.strip()): return True except re.error: pass return False # 测试 print(check_answer('delete [] arr;', 'delete[] arr;')) # True print(check_answer('delete[] arr ;', 'delete[] arr;')) # True print(check_answer('delete arr;', 'delete[] arr;')) # Falsenormalize函数做了三层处理:去首尾空白、合并连续空白、去掉标点符号周围的空白。这样delete [] arr;和delete[] arr;会被归一化成同一个字符串。正则比对是兜底,用于那些“答案不唯一”的题目,比如std::vector<int>和vector<int>在using namespace std;环境下都算对,标准答案可以写成(std::)?vector<int>。
3.3 批量判分与错题统计:让练习有反馈闭环
单题判分只是第一步,真正有价值的是批量判分后按知识点统计错误率。这样学习者能清楚看到自己哪个维度薄弱,而不是只知道“错了三道”。统计脚本在判分基础上加一个按TOPIC聚合的逻辑:
from collections import defaultdict def batch_grade(questions, user_answers): stats = defaultdict(lambda: {'total': 0, 'wrong': 0}) wrong_list = [] for q in questions: topic = q['topic'] stats[topic]['total'] += 1 user_ans = user_answers.get(q['id'], '') if not check_answer(user_ans, q['answer']): stats[topic]['wrong'] += 1 wrong_list.append({ 'id': q['id'], 'topic': topic, 'user': user_ans, 'correct': q['answer'], 'explain': q['explain'], }) return stats, wrong_list # 模拟用户答案 user_answers = {'001': 'delete[] arr;', '002': 'delete arr;'} stats, wrong = batch_grade(qs, user_answers) for topic, s in stats.items(): rate = s['wrong'] / s['total'] * 100 if s['total'] else 0 print(f'{topic}: 错误率 {rate:.0f}% ({s["wrong"]}/{s["total"]})') for w in wrong: print(f'错题 {w["id"]}: 你填了 {w["user"]},正确答案 {w["correct"]}') print(f' 解析: {w["explain"]}')这段代码输出两个东西:按知识点的错误率汇总,以及每道错题的详细对比和解析。错误率汇总让你决定下一步重点复习哪个维度,错题详情让你直接看到自己错在哪。这个闭环一旦跑通,填空题集就从“静态文档”变成了“可迭代的练习系统”。
4. 避坑与排查:填空题集落地时最容易翻车的五个地方
4.1 现象:题目解析出来少了几道,或者多出奇怪的题
原因几乎总是分隔符格式不统一。比如有的题=== QUESTION 001 ===后面多了一个空格,有的题ANSWER:写成了答案:,正则匹配不到就整块跳过。解决方法是解析前先做一次格式校验:统计=== QUESTION和=== END ===的出现次数,两者必须相等,且每个QUESTION块内必须包含TOPIC、CODE、ANSWER三个字段。校验脚本比解析脚本还短:
def validate_format(text): q_count = text.count('=== QUESTION') end_count = text.count('=== END ===') if q_count != end_count: print(f'格式错误: QUESTION 块 {q_count} 个,END 标记 {end_count} 个') return False for field in ['TOPIC:', 'CODE:', 'ANSWER:']: if text.count(field) < q_count: print(f'格式错误: {field} 出现次数少于题目数') return False return True跑一遍校验再解析,能省掉大量“为什么少了一道题”的排查时间。
4.2 现象:学习者答案明明对,系统判错
除了空白差异,最常见的坑是中文标点和英文标点混用。学习者在中文输入法下打出delete[] arr;,分号是中文全角,归一化时没处理就判错。解决方法是归一化阶段把常见中文标点替换成英文:;→;、(→(、)→)、,→,。另外,nullptr和NULL、0在某些语境下等价,如果题目不强制要求某种写法,标准答案应该用正则写成(nullptr|NULL|0)。
4.3 现象:题目难度失控,新手一题都做不出
这是出题人最容易犯的错——用自己的水平衡量新手。我一般会找一两个刚学完语法的人试做,如果某道题他们卡超过五分钟,要么降低挖空难度,要么在题目上方加一行提示(比如“提示:注意 new 和 delete 的配对形式”)。提示不算泄题,它只是把“猜谜”拉回“填空”。另外,难度标注要诚实:你觉得“入门”的题,新手可能觉得是“进阶”,以试做者的反馈为准。
4.4 现象:同一道题在不同编译器下行为不同
C++ 的未定义行为是填空题集的隐形炸弹。比如你挖空考i++和++i在同一个表达式里的求值顺序,标准答案写了一个,但实际在不同编译器上结果可能不同。这类题要么避开,要么在解析里明确写“此行为未定义,实际输出依赖编译器实现,不建议在生产代码中这样写”。填空题集考的是“确定的语言规则”,不是“编译器的脾气”。
4.5 现象:题量上去后,维护答案和解析变成负担
一开始十几道题手工维护没问题,到上百道题时,改一个知识点可能牵动多道题的解析。我的做法是给每道题加一个VERSION字段和LAST_MODIFIED日期,修改时更新版本号,并用脚本扫描“同一知识点下解析互相矛盾”的题。比如两道题都考delete和delete[]的区别,一道解析说“必须配对”,另一道说“可以混用”,脚本检测到关键词冲突就报警。这个机制不复杂,但能防止题集越做越乱。
5. 让填空题集可扩展:从静态文档到可生成、可验证的题库
5.1 用模板批量生成同知识点变体题
一套好的填空题集不应该只有固定几十道题,而应该能按知识点批量生成变体。比如“动态数组释放”这个知识点,可以生成int、double、自定义结构体三种类型的变体,挖空位置不变,只换类型和变量名。用 Python 的字符串模板就能做:
TEMPLATE = '''=== QUESTION {id} === TOPIC: 内存与指针 DIFFICULTY: 入门 CODE: {type}* arr = new {type}[{size}]; for (int i = 0; i < {size}; ++i) arr[i] = {init}; // ______ 此处补全释放语句 ANSWER: delete[] arr; EXPLAIN: 用 new[] 分配的 {type} 数组必须用 delete[] 释放。 === END ===''' variants = [ {'type': 'int', 'size': 5, 'init': 'i * i'}, {'type': 'double', 'size': 8, 'init': 'i * 1.5'}, {'type': 'char', 'size': 16, 'init': "'a' + i"}, ] for idx, v in enumerate(variants, start=1): v['id'] = f'{idx:03d}' print(TEMPLATE.format(**v))这段代码生成三道同结构但类型不同的题。TEMPLATE里的{id}、{type}等占位符由variants列表填充。你可以把variants扩展成几十个组合,一次性生成大量变体题。注意:生成后仍然要人工过一遍,确认每道题的init表达式在对应类型下合法(比如char类型下'a' + i是合法的,但i * 1.5赋给char会截断,需要调整)。
5.2 用编译验证答案:让机器确认“标准答案真的能跑”
填空题集最大的信任危机是“标准答案本身编译不过”。避免这个问题的方法很简单:把每道题的完整代码(填入标准答案后)抽出来,用g++批量编译验证。脚本逻辑是:解析题目 → 把// ______替换成ANSWER→ 补上必要的头文件和main函数 → 调用g++ -fsyntax-only检查语法。
#!/bin/bash # 批量语法检查:把每道题的答案填回去,用 g++ 验证 for f in ./generated/*.cpp; do if ! g++ -std=c++17 -fsyntax-only "$f" 2>/dev/null; then echo "编译失败: $f" g++ -std=c++17 -fsyntax-only "$f" fi done echo "检查完成"-fsyntax-only只做语法检查,不生成可执行文件,速度快。-std=c++17指定标准,避免不同默认标准导致的差异。如果某道题编译失败,要么是标准答案写错了,要么是题目本身缺少必要的上下文(比如没包含头文件)。这个步骤应该纳入出题流程,每改一道题就跑一次。
5.3 一个具体技巧:用错题反推知识点盲区
最后分享一个我常用的技巧:不要只看“错了多少”,而是看“错题集中在哪个知识点的哪个变体上”。比如“内存与指针”维度下,delete和delete[]的题全对,但“悬垂指针”的题全错,说明问题不在释放语法,而在指针生命周期理解。这时候应该补的是“指针作用域”相关的题,而不是继续刷释放题。具体做法是在统计脚本里把TOPIC再细分成SUBTOPIC,比如内存与指针/释放、内存与指针/悬垂,错题统计精确到子知识点。这个字段加在题目格式里就行,解析和统计逻辑不用大改。
我自己维护这套题库的习惯是:每加一道新题,先跑格式校验,再跑编译验证,最后自己试做一遍确认难度标注准确。这三步走完,题目才算“入库”。一开始觉得麻烦,但踩过几次“标准答案编译不过”和“难度标错劝退新手”的坑之后,这三步就成了肌肉记忆。希望帮到你。
本文还有配套的精品资源,点击获取