Files
scuc-qt-course/docs/teaching/slides/day1.html
T
张宗平 b89819d5db 新增 4 天教案大纲 + reveal.js 交互幻灯片(docs/teaching/)
大纲按「3天C++强化(含OOP)+1天Qt桌面」组织,讲:练≈1:2.7;每个知识点直接
链接仓库真实代码文件与 ERRATA/VERSION_NOTES 里记录的具体坑。幻灯片
vendor 了 reveal.js 5.2.1(核心+highlight插件),零 CDN 依赖,配 serve.py
本地起服务(仅绑定 127.0.0.1)供离线播放,代码链接可点开查看源码。
2026-07-01 16:51:12 +08:00

354 lines
14 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Day 1 · C++ 对 C 的扩展 + 类和对象基础</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="reveal/reveal.css">
<link rel="stylesheet" href="reveal/theme.css">
<link rel="stylesheet" href="reveal/plugin/highlight/monokai.css">
<link rel="stylesheet" href="custom.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Day 1</h1>
<h3>C++ 对 C 的扩展 &nbsp;+&nbsp; 类和对象基础</h3>
<p class="module-badge">Qt 方向实训 · 第 1 / 4 天 · 讲:练 ≈ 1:2.7</p>
<ul>
<li>模块 A(3 课时):C++ 对 C 的扩展 —— 强化,非入门</li>
<li>模块 B3 课时):类和对象基础</li>
</ul>
<p><small>大纲:<a class="filelink" href="../OUTLINE.md">docs/teaching/OUTLINE.md</a></small></p>
</section>
<!-- ===================== 模块 A:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 讲授 35min</p>
<h2>C++ 对 C 的扩展 <span class="tag tag-lecture"></span></h2>
<p>已默认你熟悉这些语法——今天讲「为什么」,不讲「怎么写」:</p>
<ul>
<li>namespace 的作用域本质</li>
<li>函数重载的决议机制</li>
<li>const 的本质 vs #define</li>
<li>引用的本质(语法糖视角)</li>
<li>三目运算符可否作左值 —— 纯版本差异</li>
</ul>
</section>
<section>
<p class="module-badge">强化点 1 / 5</p>
<h3>namespace 为什么不能定义在函数体内 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
void test(){
cout &lt;&lt; "A::a : " &lt;&lt; A::a &lt;&lt; endl;
cout &lt;&lt; "B::a : " &lt;&lt; B::a &lt;&lt; endl;
}
</code></pre>
<p><small>作用域是「文件级」概念,不是「语句顺序」——wiki 原文把 namespace
写进了函数体内,编译不过;出处:<a class="filelink" target="_blank"
href="../../../p01/ch03/namespace_syntax.cpp">p01/ch03/namespace_syntax.cpp</a>
修正记录 <span class="filelink">ERRATA.md [58954439:3/6/7]</span></small></p>
</section>
<section>
<p class="module-badge">强化点 2 / 5</p>
<h3>函数重载:二义性从哪来 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
void MyFunc(string b){ cout &lt;&lt; "b: " &lt;&lt; b &lt;&lt; endl; }
// 函数重载碰上默认参数
void MyFunc(string b, int a = 10){ cout &lt;&lt; "a: " &lt;&lt; a &lt;&lt; " b:" &lt;&lt; b &lt;&lt; endl; }
int main(){
// MyFunc("hello"); // 二义:两个重载同等匹配
MyFunc("hello", 20); // 显式给第二参才能消歧义
}
</code></pre>
<p><small>重载决议只看「能否唯一确定最佳匹配」,默认参数不参与决议——
出处:<a class="filelink" target="_blank"
href="../../../p01/ch03/function_overload_basics_2.cpp">p01/ch03/function_overload_basics_2.cpp</a>
<span class="filelink">ERRATA.md [58954439:42] ambiguous-overload</span></small></p>
</section>
<section>
<p class="module-badge">强化点 3 / 5</p>
<h3>const 的本质 vs #define <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
const int constA = 10;
int main(){
int* p = (int*)&constA;
*p = 200; // 绕过 const 的“建议性”检查——编译期常量 ≠ 只读内存
}
</code></pre>
<p><small>const 是「编译期类型检查」,不是「运行期内存保护」;#define 只是文本替换,
没有类型、没有作用域。出处:<a class="filelink" target="_blank"
href="../../../p01/ch03/const_diff_summary.cpp">p01/ch03/const_diff_summary.cpp</a></small></p>
</section>
<section>
<p class="module-badge">强化点 4 / 5</p>
<h3>引用的本质 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
void testFunc(int& ref){
ref = 100; // 编译器内部转换为 *ref = 100
}
int main(){
int a = 10;
int& aRef = a; // 自动转换为 int* const aRef = &a —— 这也是引用必须初始化的原因
aRef = 20; // 内部转换为 *aRef = 20
}
</code></pre>
<p><small>把引用当成「自动解引用的 const 指针」来理解,很多“为什么引用不能重新绑定”
的疑惑会自然消失。出处:<a class="filelink" target="_blank"
href="../../../p01/ch03/reference_essence.cpp">p01/ch03/reference_essence.cpp</a></small></p>
</section>
<section>
<p class="module-badge">强化点 5 / 5 · 版本差异</p>
<h3>三目运算符能否作左值 —— 纯版本问题 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
// Cp01/ch03/ternary_operator.c):三目表达式返回右值
(a &gt; b ? a : b) = 100; // C 中非法
// C++p01/ch03/ternary_operator.cpp):三目表达式返回左值引用
(a &gt; b ? a : b) = 100; // C++ 中合法,b 会被改写
</code></pre>
<p><small>现场演示:<code class="filelink">tools/std_probe.py --lang c --file
p01/ch03/ternary_operator.c --standards c89,c99,c11,c17 --pedantic-errors</code>
—— 对照 <span class="filelink">docs/VERSION_NOTES.md §2</span></small></p>
</section>
</section>
<!-- ===================== 模块 A:练 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 练习 95min</p>
<h2>动手:ch03 强化练习 <span class="tag tag-practice"></span></h2>
<p>目录:<a class="filelink" target="_blank" href="../../../p01/ch03">p01/ch03/</a>
(47 个源文件,本节任务只挑一部分,其余留作参考自学)</p>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · 逐一验证 <span class="tag tag-practice"></span></h4>
<p><code class="filelink">p01/ch03</code> 挑 810 个文件(namespace / reference /
overload / const 系列),逐个执行:</p>
<pre><code class="bash" data-trim>tools/run.sh p1c03_03 # namespace_syntax
tools/run.sh p1c03_33 # reference_essence
tools/run.sh p1c03_43 # function_overload_basics_2
# ……自行在 build/ 里用 ninja -t targets 或 cmake --build 找到其余目标名</code></pre>
<p>对照 <code class="filelink">docs/ERRATA.md</code> 对应条目,写一句话解释
「wiki 原文错在哪、现在为什么对」。</p>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 2 · 版本探针实操 <span class="tag tag-practice"></span></h4>
<pre><code class="bash" data-trim>tools/std_probe.py --lang c --file p01/ch03/ternary_operator.c \
--standards c89,c99,c11,c17 --pedantic-errors</code></pre>
<p>记录哪个标准报错、哪个不报错,对照
<code class="filelink">docs/VERSION_NOTES.md §2</code> 的结论,理解“同一段代码
在不同标准下含义不同”。</p>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 3 · 拔高(选做) <span class="tag tag-practice"></span></h4>
<p>阅读 <code class="filelink">docs/ERRATA.md [58954439:42]</code>,解释生成脚本的
去重逻辑为什么会把重载函数改名导致二义性——这是一次「工具误伤正确代码」的真实案例,
训练读 diff、读生成器逻辑的能力。</p>
<p><small>参考:<a class="filelink" target="_blank"
href="../../../tools/gen_part1.py">tools/gen_part1.py</a><code>dedupe</code> 相关逻辑</small></p>
</div>
</section>
</section>
<!-- ===================== 模块 B:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 讲授 35min</p>
<h2>类和对象基础 <span class="tag tag-lecture"></span></h2>
<ul>
<li>封装:C 结构体 vs C++ 类</li>
<li>构造 / 析构的调用时机</li>
<li>深拷贝 vs 浅拷贝</li>
<li>静态成员与单例</li>
</ul>
</section>
<section>
<h3>封装:同一个问题,两种语言写法 <span class="tag tag-lecture"></span></h3>
<pre><code class="c" data-trim data-noescape>
// C:结构体没有访问控制,甚至类型都保护不了
typedef struct _Person{ char name[64]; int age; } Person;
typedef struct _Aninal{ char name[64]; int age; int type; } Ainmal;
void AnimalEat(Ainmal* animal){ printf("%s在吃动物吃的饭!\n", animal->name); }
int main(){
Person person; strcpy(person.name, "小明"); person.age = 30;
AnimalEat(&person); // 把 Person* 传给期望 Ainmal* 的函数——编译器毫无察觉
}
</code></pre>
<p><small>对照:<a class="filelink" target="_blank"
href="../../../p01/ch04/s01/class_encapsulation.c">class_encapsulation.c</a> vs
<a class="filelink" target="_blank"
href="../../../p01/ch04/s01/class_encapsulation.cpp">class_encapsulation.cpp</a>
public/protected/private 三级访问控制)</small></p>
</section>
<section>
<h3>构造 / 析构的调用时机 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
class Person{
public:
Person(){
cout &lt;&lt; "构造函数调用!" &lt;&lt; endl;
pName = (char*)malloc(sizeof("John"));
strcpy(pName, "John");
}
~Person(){
cout &lt;&lt; "析构函数调用!" &lt;&lt; endl;
if (pName != NULL){ free(pName); pName = NULL; }
}
private:
char* pName;
};
</code></pre>
<p><small>强化点:wiki 原文这一串例子普遍漏写 <code>#include &lt;cstring&gt;</code>
(用到 strcpy 却不声明)——出处 <a class="filelink" target="_blank"
href="../../../p01/ch04/s03/ctor_and_dtor.cpp">p01/ch04/s03/ctor_and_dtor.cpp</a>
<code class="filelink">ERRATA.md [58954451:0..13] missing-include</code>:编译器不报错,
但换个平台/换个头文件包含顺序就可能炸——这类隐式依赖正是“强化”该抓的细节。</small></p>
</section>
<section>
<h3>深拷贝 vs 浅拷贝 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
class Person{
public:
Person(char* name, int age){
pName = (char*)malloc(strlen(name) + 1);
strcpy(pName, name);
}
// 自己实现拷贝构造函数 —— 深拷贝
Person(const Person& person){
pName = (char*)malloc(strlen(person.pName) + 1);
strcpy(pName, person.pName);
}
~Person(){ if (pName != NULL) free(pName); }
private:
char* pName;
};
void test(){
Person p1("Edward", 30);
Person p2 = p1; // 不写拷贝构造 → 默认浅拷贝 → 两个指针指向同一块内存 → 析构两次 double free
}
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../../../p01/ch04/s03/deep_vs_shallow_copy.cpp">p01/ch04/s03/deep_vs_shallow_copy.cpp</a></small></p>
</section>
<section>
<h3>静态成员与单例 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
class Printer{
public:
static Printer* getInstance(){ return pPrinter; }
void PrintText(string text){ cout &lt;&lt; text &lt;&lt; endl; mTimes++; }
private:
Printer(){ mTimes = 0; }
Printer(const Printer&){} // 禁止拷贝
private:
static Printer* pPrinter;
int mTimes;
};
Printer* Printer::pPrinter = new Printer; // 类外初始化静态成员
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../../../p01/ch04/s03/static_singleton.cpp">p01/ch04/s03/static_singleton.cpp</a></small></p>
</section>
</section>
<!-- ===================== 模块 B:练 ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 练习 95min</p>
<h2>动手:类和对象 <span class="tag tag-practice"></span></h2>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · 扩展一个类 <span class="tag tag-practice"></span></h4>
<p><a class="filelink" target="_blank"
href="../../../p01/ch04/s02/point_and_circle.cpp">point_and_circle.cpp</a>
<a class="filelink" target="_blank"
href="../../../p01/ch04/s02/cube_class_design.cpp">cube_class_design.cpp</a> 为起点,
新增一个成员函数(如计算面积 / 体积),编译运行验证输出正确。</p>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 2 · 复现并修复浅拷贝崩溃 <span class="tag tag-practice"></span></h4>
<p>修改 <a class="filelink" target="_blank"
href="../../../p01/ch04/s03/deep_vs_shallow_copy.cpp">deep_vs_shallow_copy.cpp</a>
故意删掉拷贝构造函数触发浅拷贝,用 <code class="filelink">tools/run.sh</code>
观察 double free / 崩溃或异常输出,再恢复深拷贝对比。</p>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 3 · 独立实现单例 <span class="tag tag-practice"></span></h4>
<p>参照 <a class="filelink" target="_blank"
href="../../../p01/ch04/s03/static_singleton.cpp">static_singleton.cpp</a>
独立写一个单例类(拷贝构造设为私有 / delete,验证多次 <code>getInstance()</code>
返回同一地址)。</p>
</div>
</section>
</section>
<!-- ===================== 收尾 ===================== -->
<section>
<h2>当日产出验证</h2>
<div class="verify-box">
<h4>验收标准</h4>
<pre><code class="bash" data-trim>cmake --build build --target all_p01_ch03
cmake --build build --target all_p01_ch04_s03</code></pre>
<p>全绿;至少完成 1 个「浅拷贝崩溃复现 + 修复对比」。</p>
</div>
<p class="footer-note"><small>明日预告:Day 2 —— OOP 进阶(对象模型 / 友元 /
运算符重载 / 继承 / 多态)</small></p>
<p><a class="filelink" href="index.html">返回目录</a></p>
</section>
</div>
</div>
<script src="reveal/reveal.js"></script>
<script src="reveal/plugin/highlight/highlight.js"></script>
<script>
Reveal.initialize({
hash: true,
controls: true,
progress: true,
center: true,
slideNumber: true,
plugins: [ RevealHighlight ]
});
</script>
</body>
</html>