C++ / MFC / Qt / C# 核心知识点汇总笔记
C / MFC / Qt / C# 核心知识点汇总笔记目录浅拷贝 深拷贝 内存分布new 底层做了什么类有指针成员为什么必须重载赋值运算符Qt 信号槽 符号含义 函数指针原理MFC _T() / TCHAR / w宽字符 详解MFC 为什么大量用 #define 宏MFC 为什么不用虚函数、偏用宏做消息映射WinForm 与 MFC 设计差异模态对话框 含义 由来常量指针 指针常量 区分1. 浅拷贝 深拷贝 内存分布浅拷贝编译器默认拷贝构造/默认赋值只按字节逐成员赋值。类中有裸指针时只拷贝指针地址不新开堆内存。两个对象指针指向同一块堆内存修改一个对象的指针内容另一个跟着变析构时重复释放同一块内存 → 程序崩溃深拷贝手动自定义拷贝构造、重载赋值运算符不直接赋值指针地址重新new/malloc开辟独立堆内存再把内容拷贝过去内存分布对比浅拷贝内存栈区p1、p2 对象各自独立 p1.pName 、p2.pName 存同一个堆地址 堆区只有一份字符串内容两人共用深拷贝内存栈区p1、p2 对象独立 堆区p1 一份内存、p2 单独新开一份内存 各自指向独立堆空间互不干扰修改 p1.pName 影响浅拷贝改 p1 会连带改 p2深拷贝改 p1完全不影响 p22. new 底层做了什么Person* p new Person(xxx,20);底层三件事在堆内存申请一块足够存放对象的空间调用类构造函数初始化成员把堆起始地址返回给栈上的指针p对象内存位置指针变量p在栈Person 对象本体在堆对象内指针成员再指向另一块堆字符串delete 底层做什么先调用析构函数释放内部指针堆内存释放 new 申请的对象本身堆内存关键new 出来的堆对象不会自动析构必须手动 delete。3. 类有指针成员 必须重载运算符结论类包含裸指针、构造里手动new/malloc、析构要delete/free必须自定义拷贝构造 赋值运算符重载 析构C三法则不重载的后果默认赋值是浅拷贝指针同指向一块堆内存旧内存丢失 →内存泄漏析构重复释放 →程序崩溃什么时候不用重载成员都是int/double/std::string等无裸指针、无手动堆内存申请。用std::string替代char*可彻底避开浅拷贝问题。4. Qt 信号槽 符号 函数指针底层Qt信号槽 对应 C#Qtsignals C#eventQtslots C# 普通订阅方法Qtconnect() C#绑定Qtemit C#Invoke()触发 在 类::函数名 含义普通函数名本身就是地址可隐式转函数指针类成员函数不能隐式转地址C语法强制加Sender::mySignal显式取成员函数入口地址当做函数指针传参为什么成员函数必须加成员函数隐含this指针不是独立裸函数类有函数重载A::f有语法歧义分不清重载版本成员函数指针不是单纯地址是复合结构C标准禁止成员函数名隐式转地址信号槽底层原理本质哈希映射表 成员函数指针遍历调用connect把 信号地址、接收对象、槽函数指针 存入映射表emit查表 → 遍历所有绑定的槽 → 逐个通过函数指针调用跨线程信号槽不是直接调用而是封装成消息入队列在目标线程执行。函数指针怎么知道函数起止内存函数指针只存起始地址不需要存结束地址CPU 执行到汇编ret指令自动结束函数若要人为获取起止需解析PE文件/编译器扩展。5. MFC _T() / TCHAR / 宽字符_T() 作用适配ANSI(多字节)/Unicode(宽字符)双字符集一套代码通用。底层宏定义#ifdef_UNICODE#define_T(x)L##x#else#define_T(x)x#endifUnicode_T(abc)→Labc多字节_T(abc)→abcw 前缀含义w wide 宽字符char1字节窄字符wchar_t2字节宽字符可存中文L字符串宽字符串字面量wcslen/wcscpy宽字符字符串函数配套通用类型TCHAR自动适配 char / wchar_tLPCTSTR/LPTSTR自适应字符串指针_tcsxxx自适应字符串函数三种字符串区别xxx固定 char 窄字符串Lxxx固定 wchar_t 宽字符串_T(xxx)编译时自动适配6. MFC 为什么大量用 #define 宏适配ANSI/Unicode双字符集最主要简化极复杂C语法消息映射宏兼容不同编译器、VC版本、32/64位架构功能开关裁剪控制编译模块生成重复模板代码减少手写量7. MFC 为什么不用虚函数偏用宏做消息映射年代硬件差全做虚函数虚表内存开销爆炸99%消息用不到却要全承载Windows 消息无限新增虚函数基类要频繁改动、全部重编译虚函数编译期绑定不支持运行时消息路由、动态拦截要兼容原生C语言窗口回调机制虚函数适配困难宏可自动生成映射表模板代码统一规范8. WinForm 对比 MFCWinForm 高层封装彻底屏蔽原生Win32消息无宏、无消息映射C# 原生事件委托直接替代 MFC 消息映射硬件性能充足不在乎虚表内存开销直接用虚函数重写WinForm 底层封装Win32上层只暴露Click/Load/Paint等高阶事件WinFormShow()非模态 /ShowDialog()模态9. 模态对话框 含义 由来通俗理解模态强制锁定在当前弹窗模式必须处理完关闭后才能操作父窗口非模态弹窗打开仍可自由操作主窗口。底层原理模态禁用父窗口内部独立消息循环阻塞等待关闭非模态不禁用父窗口共用消息循环调用方式MFC模态DoModal()非模态Create()ShowWindow()WinForm模态ShowDialog()非模态Show()模态词源源自拉丁语modus模式/状态逻辑学、语法情态动词引申为强制、必须遵从当前模式GUI借用该概念强制用户停留在弹窗交互模式。10. 常量指针 指针常量记忆规则const离谁近谁不可修改const在*左常量指针const在*右指针常量。常量指针constint*p;指向的值不可改*p 100报错指针本身指向可以改p b口诀指向常量指针能变指针常量int*constp;指针本身指向不可改指向的值可以改*p 100口诀指针本身是常量指向不能变双重锁定constint*constp;值不能改、地址也不能改。C 指针、数组、数组指针、指针数组、[]与.- 全套精讲一、基础代码示例classPerson{public:string name;intage;Person(string n,inta):name(n),age(a){}voidshowPerson(){coutname ageendl;}};// 动态开辟对象数组Person*arr2newPerson[2]{Person(lucy,18),Person(bob,20)};// 四种等价调用(*(arr20)).showPerson();arr2[0].showPerson();(arr21)-showPerson();(*(arr21)).showPerson();delete[]arr2;二、为什么Person* arr2可以用arr2[0]1. 核心等价公式C 铁律p[i]*(pi)只要是指针指向连续同类型内存都可以用[下标]写法。2. 逐层推导arr2类型Person*指向数组首元素的指针arr2 0还是Person*指针*(arr2 0)解引用拿到实体对象 Personarr2[0]等价于*(arr20)→ 本身就是对象3. 关键区分arr2 i→ 指针Person*arr2[i]→ 实体对象Person三、为什么arr2[0]是对象不是指针arr2是Person*存的是首元素地址arr2[0]自带隐式解引用*解引用后访问到内存里真实的 Person 实例类型从Person*变成Person通俗比喻arr2 是手指指针arr20 还是手指arr2[0] 顺着手指找到真人实体对象四、.和-运算符终极规则1. 使用铁律实体对象 / 引用→ 用.对象指针→ 用-2. 等价对照// 指针 - 用 -(arr20)-showPerson();// 解引用得到对象 - 用 .(*arr2).showPerson();arr2[0].showPerson();(*(arr20)).showPerson();3. 错误写法(*arr2)-showPerson();// 错误*arr2 是对象不能用 -arr2[0]-showPerson();// 错误arr2[0] 是对象不能用 -五、指针数组 vs 数组指针彻底分清前置优先级[]优先级高于*1. 指针数组写法Person* arr[2]无括号arr[2]先结合 → 是数组每个元素类型Person*→ 数组里存指针本质数组装指针内存多个指针可指向零散不连续对象。2. 数组指针写法Person (*p)[2]括号把*p包起来 → 先结合成指针后面[2]→ 指向一整个含2个Person的数组本质指针指向整个数组内存一整块连续数组p直接跳过整个数组大小。3. 普通指针你代码里的写法Person* arr2既不是指针数组也不是数组指针只是指向数组首元素的普通对象指针arr2只跳过一个 Person 对象大小4. 三者一眼区分写法名称本质Person* arr2普通元素指针指向单个/数组首元素Person* arr[2]指针数组数组元素是指针Person (*p)[2]数组指针指针指向整个数组5. 记忆口诀括号包星数组指针无括号先数组指针数组六、关键类型汇总表达式类型用法arr2Person*指针arr20Person*指针用-*arr2Person对象用.arr2[0]Person对象用.七、一句话终极总结指针可以用[]arr2[i] *(arr2i)带下标[]就是隐式解引用得到实体对象用.不带下标、变量i是指针用-无括号是指针数组带括号包星是数组指针Person* arr2只是普通首元素指针不属于上面两者