Qt 方向实训 · 第 1 / 4 天 · 讲:练 ≈ 1:2.7
模块 A · 讲授 35min
已默认你熟悉这些语法——今天讲「为什么」,不讲「怎么写」:
强化点 1 / 5
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
void test(){
cout << "A::a : " << A::a << endl;
cout << "B::a : " << B::a << endl;
}
作用域是「文件级」概念,不是「语句顺序」——wiki 原文把 namespace 写进了函数体内,编译不过;出处:p01/ch03/namespace_syntax.cpp, 修正记录 ERRATA.md [58954439:3/6/7]
强化点 2 / 5
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); // 显式给第二参才能消歧义
}
重载决议只看「能否唯一确定最佳匹配」,默认参数不参与决议—— 出处:p01/ch03/function_overload_basics_2.cpp, ERRATA.md [58954439:42] ambiguous-overload
强化点 3 / 5
const int constA = 10;
int main(){
int* p = (int*)&constA;
*p = 200; // 绕过 const 的“建议性”检查——编译期常量 ≠ 只读内存
}
const 是「编译期类型检查」,不是「运行期内存保护」;#define 只是文本替换, 没有类型、没有作用域。出处:p01/ch03/const_diff_summary.cpp
强化点 4 / 5
void testFunc(int& ref){
ref = 100; // 编译器内部转换为 *ref = 100
}
int main(){
int a = 10;
int& aRef = a; // 自动转换为 int* const aRef = &a —— 这也是引用必须初始化的原因
aRef = 20; // 内部转换为 *aRef = 20
}
把引用当成「自动解引用的 const 指针」来理解,很多“为什么引用不能重新绑定” 的疑惑会自然消失。出处:p01/ch03/reference_essence.cpp
强化点 5 / 5 · 版本差异
// 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 会被改写
现场演示:tools/std_probe.py --lang c --file
p01/ch03/ternary_operator.c --standards c89,c99,c11,c17 --pedantic-errors
—— 对照 docs/VERSION_NOTES.md §2
模块 A · 练习 95min
目录:p01/ch03/ (47 个源文件,本节任务只挑一部分,其余留作参考自学)
从 p01/ch03 挑 8–10 个文件(namespace / reference /
overload / const 系列),逐个执行:
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 找到其余目标名
对照 docs/ERRATA.md 对应条目,写一句话解释
「wiki 原文错在哪、现在为什么对」。
tools/std_probe.py --lang c --file p01/ch03/ternary_operator.c \
--standards c89,c99,c11,c17 --pedantic-errors
记录哪个标准报错、哪个不报错,对照
docs/VERSION_NOTES.md §2 的结论,理解“同一段代码
在不同标准下含义不同”。
阅读 docs/ERRATA.md [58954439:42],解释生成脚本的
去重逻辑为什么会把重载函数改名导致二义性——这是一次「工具误伤正确代码」的真实案例,
训练读 diff、读生成器逻辑的能力。
参考:tools/gen_part1.py 中 dedupe 相关逻辑
模块 B · 讲授 35min
// 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* 的函数——编译器毫无察觉
}
对照:class_encapsulation.c vs class_encapsulation.cpp (public/protected/private 三级访问控制)
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;
};
强化点:wiki 原文这一串例子普遍漏写 #include <cstring>
(用到 strcpy 却不声明)——出处 p01/ch04/s03/ctor_and_dtor.cpp,
ERRATA.md [58954451:0..13] missing-include:编译器不报错,
但换个平台/换个头文件包含顺序就可能炸——这类隐式依赖正是“强化”该抓的细节。
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
}
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; // 类外初始化静态成员
模块 B · 练习 95min
以 point_and_circle.cpp 或 cube_class_design.cpp 为起点, 新增一个成员函数(如计算面积 / 体积),编译运行验证输出正确。
修改 deep_vs_shallow_copy.cpp:
故意删掉拷贝构造函数触发浅拷贝,用 tools/run.sh
观察 double free / 崩溃或异常输出,再恢复深拷贝对比。
参照 static_singleton.cpp,
独立写一个单例类(拷贝构造设为私有 / delete,验证多次 getInstance()
返回同一地址)。
cmake --build build --target all_p01_ch03
cmake --build build --target all_p01_ch04_s03
全绿;至少完成 1 个「浅拷贝崩溃复现 + 修复对比」。
明日预告:Day 2 —— OOP 进阶(对象模型 / 友元 / 运算符重载 / 继承 / 多态)