Files
scuc-qt-course/tools/teaching_ppt/day1.py
T
张宗平 ea6f12e7ac PPT 版本幻灯片:生成器 + 5 天讲稿备注(已去 AI 味)
- tools/teaching_ppt/gen.py:python-pptx 生成引擎(页面内容与讲稿数据分离,
  幂等可重生成);tools/teaching_ppt/day1..5.py:每天的页面+讲稿数据
- docs/teaching/ppt/day1..5.pptx:5 份 PPT 产物,页面内容对应 slides/*.html,
  详细讲稿(建议时长/讲法/演示/互动/过渡)只写入演讲者备注,不进主页面
- 讲稿去 AI 味:破折号过度堆砌 152 处砍到 6 处(7.8‰ → 0.3‰)、
  升华腔(分水岭/点燃探索欲)清零、重复词(现场 26→11)替换;
  保留教学口语生动感(你们/当堂/点破/埋点/回指)与【过渡】悬念钩子
- 生成方式:python-pptx 在隔离 venv 装,不影响系统 Python;
  改 day*.py 的 notes 字段后重跑 gen.py 即可幂等重生成
2026-07-05 16:08:13 +08:00

311 lines
22 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""Day1 · 开训 + C++ 回顾与提升(全天 C++)—— 页面对应 slides/day1.html。"""
SLIDES = [
{
'kind': 'title',
'badge': 'C++/Qt 方向实训 · 第 1 / 4 个讲授日 · 对齐 2026-07 新版 10 天安排',
'title': 'Day 1 · 开训 + C++ 回顾与提升',
'subtitle': '全天 C++:技术版图 / 环境与 CMake / OOP / 继承多态 / 内存管理 / STL',
'bullets': [
'上午 08:3011:00 讲授:开训 · C++ 技术版图 · 环境与 CMake · PRETEST 摸底 · OOP 回顾',
'上午 11:0012:00 课堂练习:独立完成 Part1 构建(144 目标全绿)',
'下午 13:30–16:00 讲授:继承与多态 · 内存管理与所有权 · STL · 现代 C++ 巡礼',
'下午 16:0017:00 作业:ERRATA 修坑讲解 + 版本探针(选做)',
],
'source': '大纲:docs/teaching/OUTLINE_4DAY.md · 摸底卷:docs/teaching/PRETEST.md',
'minutes': 3, 'segment': '开场',
'notes': """【定位】今天全天 C++,是"回顾和提升",不是零基础教学。开场就把这话挑明:基础好的别嫌无聊,基础弱的别慌,今天讲的是"学过但容易讲不透"的那些点。
【时长骨架】上午:开训典礼约 40 分钟(组织方)→ 技术版图讲座 25 → 环境与 CMake 25 → 编译vs链接 10 → PRETEST 摸底 10 → OOP 回顾 25,留 15 分钟机动。下午:讲授 6 页约 90 分钟,穿插 3 次 15 分钟随堂练,再留 15 分钟机动。
【讲法】本页只念四行安排,强调两点:每半天都有动手环节;今天的产出(构建全绿)是后面四天所有演示的前提。"""
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': 'C++ 常用技术和框架(讲座)',
'boxes': [
('concept', '核心概念——C++ 的四大应用版图', [
'桌面客户端:Qt(本次实训主线)、跨平台 GUI',
'服务端/基础设施:高性能网络服务、数据库引擎、游戏服务器',
'嵌入式/车载:资源受限环境的第一梯队语言',
'游戏/图形:UE 引擎、渲染管线',
]),
('info', '而且它还在进化', [
'C++11 起三年一版:11「几乎新语言」→ 17 实用大件 → 20 四大件',
'自学入口:docs/teaching/MODERN_CPP.md · MODERN_CPP_20PLUS.md(今天下午末尾回收)',
]),
],
'minutes': 25, 'segment': '上午 · 讲座(开训典礼后)',
'notes': """【开场】开训典礼刚结束,注意力还散着。用一个提问把它接回来:"你们手机和电脑里,哪些软件是 C++ 写的?"微信桌面版、Chrome 内核、各种游戏引擎,基本都躲不开。
【讲法】四大版图每个讲 4–5 分钟:举一个具体产品,再说清楚这个领域为什么离不开 C++(性能可控、贴近硬件、生态成熟)。讲到桌面版图时落到 Qt,预告一句:"明天开始四天全是它。"
【第二个盒子】只花 3 分钟。告诉学员 C++ 不是老古董,三年一版在进化;两份仓库内文档是自学材料,下午末尾还会回来点一次,现在不展开。
【过渡】"要用这门语言干活,先得把工程环境立起来。下一页,构建工具。" """
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': 'CMake:用本仓库当教材',
'code': 'cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF -DCMAKE_BUILD_TYPE=Debug\ncmake --build build # 144 个目标全绿',
'boxes': [
('concept', None, [
'CMake 是「生成构建系统的工具」不是构建系统本身:-S -B 生成 Ninja/Makefile 工程,--build 才是真正编译',
'Makefile 是 CMake 的生成目标之一——这就是两者的关系',
'option(BUILD_QT_PART) 控制是否编 Qt 部分;每个示例一个 add_executable 目标',
]),
('pitfall', None, [
'换生成器/清缓存后要重新走一次 -S -B(大多数普通改动 CMake 会自动侦测)',
]),
('info', '包管理器一分钟', [
'vcpkg:装库 + toolchain 文件 → find_package 自动定位;本仓库选「隔离 Qt 手动装」——两种思路都要认识',
]),
],
'source': '出处:仓库根 CMakeLists.txt',
'minutes': 25, 'segment': '上午 · 环境与构建',
'notes': """【讲法】本页是演示页,不是念 PPT:切到终端,真敲上面两条命令(预先建过一次 build,第二次跑很快),学员就能看到 144 个目标滚屏全绿。
【关键区分】"生成""构建"两阶段用装修类比:CMakeLists 是图纸,-S -B 是出施工方案(Ninja 文件),--build 才是开工。学员以后 90% 的 CMake 困惑,都出在分不清这两步。
【Makefile 的处理】新安排点名了 Makefile,这里用一句话安放它:打开 build/ 目录看生成产物,告诉学员"你们以前手写的 Makefile,在 CMake 工作流里是被生成出来的",不单独展开手写语法。
【包管理器】最后 2 分钟带过 vcpkg 概念即可。本仓库用隔离安装,明天装 Qt 时会亲眼看到。
【过渡】"构建会失败,失败分两种。下一页教你一眼分辨。" """
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': '★ 编译错误 vs 链接错误:排查方向完全不同',
'code': "main.cpp:12:5: error: 'foo' was not declared <- 编译期:带文件行号\nmain.o: undefined reference to `Widget::bar()' <- 链接期:没有行号",
'boxes': [
('concept', None, [
'error: 带行号 = 编译期(语法/类型/没声明)——回代码查那一行',
'undefined reference 无行号 = 链接期(有声明没定义/缺库/缺目标文件)——查「定义在哪、链没链进来」',
]),
('pitfall', None, [
'见到 undefined reference 就回头查语法拼写——方向错了',
'伏笔:明天 Qt 忘写 Q_OBJECT 报的正是链接错误,且报错完全不提宏',
]),
],
'minutes': 10, 'segment': '上午 · 环境与构建',
'notes': """【讲法】把上面两行报错投在屏幕上,让学员找不同:一个有 main.cpp:12:5,一个只有 main.o。就用"有没有行号"这个最粗暴的判据,帮他们建立第一反应。
【为什么现在讲】这不是语法点,是排错元技能。今天下午写练习、明天起写 Qt,报错会立刻大量出现,先给分类器再给知识。
【伏笔要点破】明确预告:明天会有一个"忘写宏导致链接错误"的经典坑,报错信息完全不提宏。到时候谁先想起今天这页,谁先解决。
【过渡】"讲了半天,你们的底子到底怎么样?测一下,10 分钟。" """
},
{
'badge': '上午 · 摸底 10 分钟',
'title': 'PRETEST:10 题摸底(闭卷、独立完成)',
'bullets': [
'双平行卷 A/B 交错发放,防抄袭;语言基线 C++98,只测经典功底',
'三级梯度:L1 基础语法(1–3 题)/ L2 OOP 与内存(4–7 题)/ L3 多态细节(8–10 题)',
],
'boxes': [
('info', '测完怎么用', [
'L1 大面积错 → 回退预案(放慢 OOP 回顾、下午 STL 让位基础加固)',
'L2 部分错 → 对应知识点讲授时加时;L3 全对 → 全速推进',
]),
],
'source': '卷面与分层预案:docs/teaching/PRETEST.md',
'minutes': 10, 'segment': '上午 · 摸底',
'notes': """【操作】A/B 卷按座位交错发。规则宣布清楚:闭卷、不查资料、10 分钟,答不出就空着。这是给教学定速用的,不计成绩(这句必须说,否则有人抄有人慌)。
【计时】投屏倒计时 10 分钟。期间不讲课,巡场看答题情况,L1 三道题扫一眼就能看出班级底色。
【收卷后】当场只判 L1(30 秒能判完一摞)。L1 通过率高就按原速走;低于一半,启用 PRETEST 里的回退预案:下午 STL 压缩为 10 分钟点名,把时间还给继承与多态。L2/L3 课后判,用于明天微调。
【过渡】"卷子里的问题,接下来两个小时会逐个讲透。先从最基本的:类和封装。" """
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': 'OOP 回顾:类 / 对象 / 封装 + namespace',
'boxes': [
('concept', None, [
'封装 = 数据和操作绑定 + 访问控制(public/private 是给「人」划的边界,不是安全机制)',
'构造/析构对应对象生命周期的两端——这个模型下午讲「所有权」还要用',
'namespace 的「作用域」不是「文件顺序」:可跨文件延续、跨块合并',
]),
('pitfall', None, [
'头文件里 using namespace std; 会把污染带给所有 include 它的文件——只在 .cpp 用,或全程写限定名',
]),
],
'source': '出处:p01/ch04/(类与对象各节)· p01/ch03/namespace_syntax.cpp · 对照 docs/ERRATA.md',
'minutes': 25, 'segment': '上午 · OOP 回顾(按 PRETEST 结果调速)',
'notes': """【调速点】这一页的深度看刚才 L1 判卷结果:底子好,10 分钟把三个概念点过,多出的时间给学员提问;底子弱,打开 p01/ch04 的源文件逐个走读,讲满 25 分钟。
【讲法】封装讲"边界":private 挡的是队友的手滑,不是黑客,工程含义大于语言含义。构造/析构用"出生登记/注销"类比,并预告:下午所有权、明天 Qt 父子树,全在这条线上。
【namespace】重点讲 ERRATA 里的真实修正案例(wiki 原文把作用域理解成文件顺序)。打开 docs/ERRATA.md 对应条目,让学员看到"教材也会错,错误可以被记录和修正"。这也是下午作业的预演。
【过渡】"上午最后一小时,轮到你们上手。任务卡在下一页。" """
},
{
'badge': '上午 · 课堂练习 11:00–12:00(工程师指导、答疑)',
'title': '任务卡 1 · 走一遍真实构建流程',
'boxes': [
('task', '任务卡 1', [
'目标:独立完成本仓库 Part1 的配置+构建,不看着教师操作',
'验收:cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF 配置成功;cmake --build build 144 目标全绿',
'验收:能说出 option(BUILD_QT_PART) 的作用',
'参考:仓库根 CMakeLists.txt · CMake 官方 Tutorial(选读·需联网)',
]),
],
'minutes': 60, 'segment': '上午 · 课堂练习',
'notes': """【组织】学员独立操作,教师和助教巡场。预期 20 分钟内多数人完成两条命令;剩下时间处理个体环境问题(权限、Ninja 没装、路径带中文空格等,这些"环境坑"本身就是教学素材,遇到就广播讲)。
【常见卡点】①在错误目录下执行(顺势回收上午"工作目录"意识,Day4 还会正式讲);②build 目录残留旧缓存报错,正好演示"清缓存要重新 -S -B"
【提前完成者】引导做两件事:给 BUILD_QT_PART=ON 试一次(会因没装 Qt 停在配置期,预告明天装);或提前看 docs/ERRATA.md 挑下午作业要讲的条目。
【收尾 5 分钟】随机抽两人口头回答 option(BUILD_QT_PART) 的作用,达标才算过验收。"""
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '下午总览:继承多态 / 内存管理 / STL',
'bullets': [
'C++ 核心特性:函数重载决议 · 虚函数与虚析构',
'内存管理:引用的本质 · ★所有权——谁负责 delete',
'STL 常用容器 + 迭代器',
'收尾:现代 C++ 巡礼(兴趣钩子)',
],
'minutes': 3, 'segment': '下午 · 开场',
'notes': """【节奏】下午 6 个知识点约 90 分钟讲授,每讲完 2 个插一次 10–15 分钟随堂练(写几行代码验证刚讲的行为),最后留 15 分钟机动。
【主线】向学员挑明下午的暗线:所有内容都在回答"对象什么时候生、什么时候死、死的时候发生什么"。这条线明天会直接接到 Qt 的对象树上。"""
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '函数重载决议',
'boxes': [
('concept', None, [
'编译器靠名字改写(name mangling)把重载变成不同符号',
'二义性往往不是写错,而是候选集中有两个「一样好」的候选',
'重载(编译期·同类内)vs 重写(运行期·继承体系)是两码事',
]),
('pitfall', None, [
'隐式类型转换会扩大候选集——传 long 可能触发意料之外的重载版本',
]),
],
'source': '出处:p01/ch03/function_overload_mechanism.cpp',
'minutes': 15, 'segment': '下午 · 继承与多态',
'notes': """【引入】把 PRETEST 第 4 题(h(long)/h(double) 传 5)投出来重做一遍。这题当堂错误率通常最高,正好当引子。
【讲法】用 nm 或编译器输出展示一次名字改写后的符号(_Z1hl 之类),学员对"重载在符号层怎么实现"会豁然开朗,顺带把上午"链接错误查符号"的意识再钉一次。
【随堂练点】讲完本页加下一页后插第一次随堂练(15 分钟):让学员亲手制造一个二义性调用并读懂报错。
【过渡】"重载是编译期挑函数;运行期挑函数就是多态。它有个最容易漏的角落:析构。" """
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '虚函数与虚析构',
'code': 'Base* p = new Derived();\ndelete p; // 如果 ~Base() 不是 virtual~Derived() 不会被调用——经典泄漏',
'boxes': [
('concept', None, [
'经基类指针 delete 派生对象时,只有基类析构是 virtual 才会沿虚表调到派生类析构',
'这就是「多态」在析构场景下的具体体现',
]),
('pitfall', None, [
'类可能被继承 + 可能经基类指针删除 ⇒ 析构就该 virtual(防御性约定)',
'明天会看到 Qt 的 QObject 体系全线虚析构',
]),
],
'source': '出处:p01/ch04/s08/virtual_dtor_purpose.cpp',
'minutes': 18, 'segment': '下午 · 继承与多态',
'notes': """【讲法】当堂跑 p01/ch04/s08/virtual_dtor_purpose.cpptools/run.sh),先注释掉 virtual 看派生类析构不打印,再加回去看两级析构依次打印。"看见泄漏"比讲十分钟原理有效。
【原理层】底子好的班补讲虚表指针:对象头部藏着 vptr,delete 时经 vptr 找到"真正的类型"的析构。底子弱的班跳过 vptr,只记防御性约定。
【互动】提问:"STL 容器(vector)为什么没有虚析构却没事?"因为没人拿基类指针删它,规则是条件式的,不是"所有类都要 virtual"
【随堂练】此处插第一次随堂练 15 分钟:改 virtual_dtor_purpose.cpp 自己复现一次泄漏与修复。"""
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '引用的本质',
'boxes': [
('concept', None, [
'把引用理解成「自动解引用的 const 指针」——能解释为什么必须初始化、不能重新绑定',
]),
('pitfall', None, [
'返回局部变量的引用:函数结束即悬空,访问是未定义行为——和返回局部指针同一类坑',
]),
],
'source': '出处:p01/ch03/reference_essence.cpp',
'minutes': 12, 'segment': '下午 · 内存管理',
'notes': """【引入】PRETEST 第 1 题(引用能否绑 nullptr)回收:用"const 指针"视角一句话解释所有选项。
【讲法】画两张内存图:指针是"另一个格子存地址",引用是"同一个格子的别名"。但底层实现常常就是指针,语言只是不让你碰它。
【悬空引用】强调它是"时间维度"的错误:代码每一行单看都对,错在对象死了引用还活着。这正是下页"所有权"和 Day2 lambda 悬空坑的同一个病根,讲到那里会反复回指这页。
【过渡】"对象死了还被人用。那到底该谁管对象的死活?下一页,全天最重要的一页。" """
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '★ 所有权:谁负责 delete',
'boxes': [
('concept', None, [
'内存管理的核心问题不是「会不会 new」,而是「这块资源归谁管、由谁释放」',
'现代答案:RAII / 智能指针——unique_ptr 独占、shared_ptr 共享(MODERN_CPP.md §2',
'一个对象只能有一个所有者',
]),
('pitfall', None, [
'双重释放 / 忘记释放 / 释放后使用——三兄弟全部源于所有权不明',
'明天的伏笔:Qt 用「父子树」回答同一个问题,两套所有权体系不能混用',
]),
],
'minutes': 18, 'segment': '下午 · 内存管理',
'notes': """【地位】如果今天只能让学员带走一页,就是这页。所有权心智模型是"学过 C++ 语法""写得出不崩的程序"之间最大的那道坎,Day2 的三个崩溃坑全是它的变体。
【讲法】先讲问题再讲方案:举一个函数返回 new 出来的指针的例子,问"谁该 delete?调用方?被调方?文档里写了吗?"让学员体会到,"所有权不明"本身就是 bug 温床。然后给出 unique_ptr(独占,move 转移)和 shared_ptr(引用计数),作为把所有权写进类型的方案。
【尺度】智能指针只讲到"是什么、解决什么",用法细节指向 MODERN_CPP.md §2 自学。因为明天起 Qt 的世界用父子树而不是智能指针,别让学员在 Qt 代码里到处套 shared_ptr(这是真实的常见误用)。
【伏笔】用加重语气念出"两套所有权体系不能混用",预告明天上午有一个现场崩溃演示。
【随堂练】此处插第二次随堂练 15 分钟:给一段双重释放的代码,让学员用"所有权归谁"的语言说出错因。"""
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '模板与 STL 常用容器',
'boxes': [
('concept', None, [
'泛型编程:同一份代码适配不同类型;vector / map / string 是项目实作三件套',
'迭代器是「容器和算法之间的统一接口」——STL 算法能脱离具体容器复用的关键',
]),
('pitfall', None, [
'容器扩容/删元素后旧迭代器可能失效(如 vector 搬家),继续用是未定义行为',
]),
],
'source': '出处:p01/ch05/class_template_basics.cpp · p01/ch09/s03/vector_iterator.cpp',
'minutes': 15, 'segment': '下午 · STL',
'notes': """【调速点】PRETEST 弱班把本页压到 8 分钟,只讲 vector 加失效坑;强班讲满,并当堂演示一次 push_back 导致迭代器失效的未定义行为(可能崩也可能"正常",顺势又强化一次"UB 不一定崩")。
【讲法】三件套各给一个"项目里会怎么用"的场景:vector 存记录列表(Day5 案例真的用)、map 做查找表、string 到处都是。迭代器讲成"统一的游标协议"range-for 是它的糖。
【失效坑】口诀给到位:"遍历中增删,先查该容器的失效规则。"不要求背规则,要求知道有这回事、会去查。
【随堂练】第三次随堂练 15 分钟:写 10 行代码,vector 边遍历边 erase,观察行为并修复(用 erase 返回值)。"""
},
{
'badge': '下午 · 讲授 13:30–16:00 · 兴趣钩子',
'title': 'C++11/14/17/20+:一门仍在进化的语言',
'boxes': [
('concept', '每 3 年一次实打实的能力升级', [
'C++11:auto · 智能指针 · 移动语义 · lambda · 语言级并发——「现代 C++ 元年」',
'C++14:泛型 lambda · 返回类型推导 · make_unique',
'C++17:结构化绑定 · if-init · optional/variant/string_view · filesystem(本仓库基线)',
'C++20Concepts · Ranges · 协程 · 模块——「自 C++11 以来最大变革」',
]),
('pitfall', None, [
'新标准 ≠ 现在能用:生产工具链常年落后 1–2 版;用 tools/std_probe.py 实测(宿主 gcc 13.3 到 C++23',
]),
],
'source': '自学:docs/teaching/MODERN_CPP.md11/14/17,仓库基线可编译)· MODERN_CPP_20PLUS.md20/23/26 + GCC 实测矩阵)',
'minutes': 12, 'segment': '下午 · 收尾钩子',
'notes': """【定位】这页是点火不是授课,目标是让学有余力的人课后打开那两份文档,不是当堂教会任何新特性。
【讲法】每个版本挑一个"最有体感"的特性各 12 分钟:C++11 讲 lambda(明天信号槽就大量用,这个衔接要点破);C++17 讲结构化绑定的赏心悦目;C++20 讲 Concepts 把几百行模板报错变一句人话。
【演示(可选)】时间富余就当堂跑一次 tools/std_probe.py,看同一段代码在 -std=c++98 和 c++17 下一个报错一个通过——"标准差异是可以实测的"
【过渡】"今天的 C++ 之旅到此,最后一小时:作业。明天早上 08:30,Qt。" """
},
{
'badge': '下午 · 课堂作业 16:00–17:00(工程师答疑)',
'title': '作业:修一个真实的坑 + 版本探针(选做)',
'boxes': [
('task', '任务卡 2 · 修一个真实的坑', [
'目标:从 docs/ERRATA.md 挑 2 条与今天知识点相关的记录(虚析构/引用/重载类)',
'验收:能用一句话向同组同学解释「wiki 原文错在哪、为什么」,不能只念代码',
]),
('task', '任务卡 3(选做)· 版本探针', [
'目标:用 tools/std_probe.py 验证一个标准版本差异点',
'验收:跑出「该标准报错、另一标准通过」的对比,对照 docs/VERSION_NOTES.md §2',
]),
],
'minutes': 60, 'segment': '下午 · 作业',
'notes': """【组织】作业开做前花 3 分钟带学员浏览一遍 ERRATA.md 的结构(每条:原文错误→修正→原因),示范挑一条"虚析构类"的怎么讲,给出"一句话解释"的示范标准。
【巡场重点】拦住"念代码"式的讲解:追问"如果不改会发生什么?"逼出因果表述。这个表达训练到 Day10 答辩就能用上。
【选做引导】完成快的学员推 std_probe:跑通一个对比后,引导他们把结论和 VERSION_NOTES.md 印证。
【收尾】17:00 前每组出 1 人讲 1 条 ERRATA(每人 1 分钟内),当日验收随堂完成。"""
},
{
'badge': '当日收尾',
'title': '当日产出验证',
'code': 'cmake --build build --target all # Part1144 个目标全绿',
'bullets': [
'每人:构建全绿 + 2 条 ERRATA 坑点讲解(口头验收)',
'PRETEST 结果归档,决定后续调速',
'明天 Day2:Qt 概述 / 创建项目 / 第一个程序 / 信号槽 → 计算器',
],
'minutes': 5, 'segment': '收尾',
'notes': """【操作】收尾 5 分钟:口头快速过验收清单;预告明天需要装隔离 Qttools/install_qt_host.sh),机器慢的可以现在挂着下载。
【衔接】用一句话给今天收线:"今天所有内容都在讲对象的生与死。明天你们会看到,Qt 用一棵树管理成千上万个对象的生死。"留下钩子散场。"""
},
]