b89819d5db
大纲按「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)供离线播放,代码链接可点开查看源码。
354 lines
14 KiB
HTML
354 lines
14 KiB
HTML
<!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 的扩展 + 类和对象基础</h3>
|
||
<p class="module-badge">Qt 方向实训 · 第 1 / 4 天 · 讲:练 ≈ 1:2.7</p>
|
||
<ul>
|
||
<li>模块 A(3 课时):C++ 对 C 的扩展 —— 强化,非入门</li>
|
||
<li>模块 B(3 课时):类和对象基础</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 << "A::a : " << A::a << endl;
|
||
cout << "B::a : " << B::a << 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 << "b: " << b << endl; }
|
||
// 函数重载碰上默认参数
|
||
void MyFunc(string b, int a = 10){ cout << "a: " << a << " b:" << b << 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>
|
||
// C(p01/ch03/ternary_operator.c):三目表达式返回右值
|
||
(a > b ? a : b) = 100; // C 中非法
|
||
|
||
// C++(p01/ch03/ternary_operator.cpp):三目表达式返回左值引用
|
||
(a > 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> 挑 8–10 个文件(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 << "构造函数调用!" << endl;
|
||
pName = (char*)malloc(sizeof("John"));
|
||
strcpy(pName, "John");
|
||
}
|
||
~Person(){
|
||
cout << "析构函数调用!" << endl;
|
||
if (pName != NULL){ free(pName); pName = NULL; }
|
||
}
|
||
private:
|
||
char* pName;
|
||
};
|
||
</code></pre>
|
||
<p><small>强化点:wiki 原文这一串例子普遍漏写 <code>#include <cstring></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 << text << 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>
|