C++ / MFC / Qt / C# 核心知识点汇总笔记

C++ / MFC / Qt / C# 核心知识点汇总笔记

目录

  1. 浅拷贝 & 深拷贝 & 内存分布
  2. new 底层做了什么
  3. 类有指针成员为什么必须重载赋值运算符
  4. Qt 信号槽 & &符号含义 & 函数指针原理
  5. MFC _T() / TCHAR / w宽字符 详解
  6. MFC 为什么大量用 #define 宏
  7. MFC 为什么不用虚函数、偏用宏做消息映射
  8. WinForm 与 MFC 设计差异
  9. 模态对话框 含义 & 由来
  10. 常量指针 & 指针常量 区分

1. 浅拷贝 & 深拷贝 & 内存分布

浅拷贝

编译器默认拷贝构造/默认赋值,只按字节逐成员赋值

类中有裸指针时:只拷贝指针地址,不新开堆内存。

  • 两个对象指针指向同一块堆内存
  • 修改一个对象的指针内容,另一个跟着变
  • 析构时重复释放同一块内存 → 程序崩溃

深拷贝

手动自定义拷贝构造、重载赋值运算符:

  • 不直接赋值指针地址
  • 重新 new/malloc 开辟独立堆内存
  • 再把内容拷贝过去

内存分布对比

浅拷贝内存

复制代码
栈区:p1、p2 对象各自独立
p1.pName 、p2.pName 存同一个堆地址
堆区:只有一份字符串内容,两人共用

深拷贝内存

复制代码
栈区:p1、p2 对象独立
堆区:p1 一份内存、p2 单独新开一份内存
各自指向独立堆空间,互不干扰

修改 p1.pName 影响

  • 浅拷贝:改 p1 会连带改 p2
  • 深拷贝:改 p1 完全不影响 p2

2. new 底层做了什么

Person* p = new Person("xxx",20); 底层三件事:

  1. 堆内存申请一块足够存放对象的空间
  2. 调用类构造函数初始化成员
  3. 把堆起始地址返回给栈上的指针 p

对象内存位置

  • 指针变量 p:在
  • Person 对象本体:在
  • 对象内指针成员再指向另一块堆字符串

delete 底层做什么

  1. 先调用析构函数(释放内部指针堆内存)
  2. 释放 new 申请的对象本身堆内存

关键

new 出来的堆对象不会自动析构,必须手动 delete。


3. 类有指针成员 必须重载=运算符?

结论

类包含裸指针 、构造里手动 new/malloc、析构要 delete/free

👉 必须自定义:拷贝构造 + 赋值运算符重载 + 析构(C++三法则)

不重载=的后果

  1. 默认赋值是浅拷贝,指针同指向一块堆内存
  2. 旧内存丢失 → 内存泄漏
  3. 析构重复释放 → 程序崩溃

什么时候不用重载

成员都是 int/double/std::string 等,无裸指针、无手动堆内存申请。

std::string 替代 char* 可彻底避开浅拷贝问题。


4. Qt 信号槽 & &符号 & 函数指针底层

Qt信号槽 对应 C#

  • Qt signals 👉 C# event
  • Qt slots 👉 C# 普通订阅方法
  • Qt connect() 👉 C# += 绑定
  • Qt emit 👉 C# Invoke() 触发

& 在 &类::函数名 含义

  • 普通函数名本身就是地址,可隐式转函数指针
  • 类成员函数 不能隐式转地址,C++语法强制加 &
    👉 &Sender::mySignal = 显式取成员函数入口地址,当做函数指针传参

为什么成员函数必须加&

  1. 成员函数隐含 this 指针,不是独立裸函数
  2. 类有函数重载,A::f 有语法歧义,分不清重载版本
  3. 成员函数指针不是单纯地址,是复合结构
  4. C++标准禁止成员函数名隐式转地址

信号槽底层原理

本质:哈希映射表 + 成员函数指针遍历调用

  1. connect:把 信号地址、接收对象、槽函数指针 存入映射表
  2. emit:查表 → 遍历所有绑定的槽 → 逐个通过函数指针调用

跨线程信号槽:不是直接调用,而是封装成消息入队列,在目标线程执行。

函数指针怎么知道函数起止内存

  • 函数指针只存起始地址
  • 不需要存结束地址,CPU 执行到汇编 ret 指令自动结束函数
  • 若要人为获取起止,需解析PE文件/编译器扩展。

5. MFC _T() / TCHAR / 宽字符

_T() 作用

适配 ANSI(多字节) / Unicode(宽字符) 双字符集,一套代码通用。

底层宏定义

c 复制代码
#ifdef _UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif
  • Unicode:_T("abc")L"abc"
  • 多字节:_T("abc")"abc"

w 前缀含义

w = wide 宽字符

  • char:1字节窄字符
  • wchar_t:2字节宽字符,可存中文
  • L"字符串":宽字符串字面量
  • wcslen/wcscpy:宽字符字符串函数

配套通用类型

  • TCHAR:自动适配 char / wchar_t
  • LPCTSTR/LPTSTR:自适应字符串指针
  • _tcsxxx:自适应字符串函数

三种字符串区别

  • "xxx":固定 char 窄字符串
  • L"xxx":固定 wchar_t 宽字符串
  • _T("xxx"):编译时自动适配

6. MFC 为什么大量用 #define 宏

  1. 适配ANSI/Unicode双字符集(最主要)
  2. 简化极复杂C++语法(消息映射宏)
  3. 兼容不同编译器、VC版本、32/64位架构
  4. 功能开关裁剪,控制编译模块
  5. 生成重复模板代码,减少手写量

7. MFC 为什么不用虚函数,偏用宏做消息映射

  1. 年代硬件差:全做虚函数,虚表内存开销爆炸,99%消息用不到却要全承载
  2. Windows 消息无限新增,虚函数基类要频繁改动、全部重编译
  3. 虚函数编译期绑定,不支持运行时消息路由、动态拦截
  4. 要兼容原生C语言窗口回调机制,虚函数适配困难
  5. 宏可自动生成映射表模板代码,统一规范

8. WinForm 对比 MFC

  1. WinForm 高层封装,彻底屏蔽原生Win32消息,无宏、无消息映射
  2. C# 原生事件+委托 直接替代 MFC 消息映射
  3. 硬件性能充足,不在乎虚表内存开销,直接用虚函数重写
  4. WinForm 底层封装Win32,上层只暴露 Click/Load/Paint 等高阶事件
  5. WinForm:Show()非模态 / ShowDialog()模态

9. 模态对话框 含义 & 由来

通俗理解

模态 :强制锁定在当前弹窗模式,必须处理完关闭后,才能操作父窗口

非模态:弹窗打开,仍可自由操作主窗口。

底层原理

  • 模态:禁用父窗口,内部独立消息循环,阻塞等待关闭
  • 非模态:不禁用父窗口,共用消息循环

调用方式

  • MFC:模态 DoModal();非模态 Create()+ShowWindow()
  • WinForm:模态 ShowDialog();非模态 Show()

模态词源

源自拉丁语 modus 模式/状态;

逻辑学、语法情态动词引申为强制、必须遵从当前模式

GUI借用该概念:强制用户停留在弹窗交互模式。


10. 常量指针 & 指针常量

记忆规则

const 离谁近,谁不可修改;
const*左:常量指针;const*右:指针常量。

常量指针

cpp 复制代码
const int *p;
  • 指向的值 不可改 *p = 100 报错
  • 指针本身指向 可以改 p = &b
    口诀:指向常量,指针能变

指针常量

cpp 复制代码
int *const p;
  • 指针本身指向 不可改
  • 指向的值 可以改 *p = 100
    口诀:指针本身是常量,指向不能变

双重锁定

cpp 复制代码
const int *const p;

值不能改、地址也不能改。


C++ 指针、数组、数组指针、指针数组、[]与.-> 全套精讲

一、基础代码示例

cpp 复制代码
class Person
{
public:
    string name;
    int age;
    Person(string n, int a) : name(n), age(a) {}
    void showPerson() {
        cout << name << " " << age << endl;
    }
};

// 动态开辟对象数组
Person* arr2 = new Person[2]{ Person("lucy",18), Person("bob",20) };

// 四种等价调用
(*(arr2 + 0)).showPerson();
arr2[0].showPerson();
(arr2 + 1)->showPerson();
(*(arr2 + 1)).showPerson();

delete[] arr2;

二、为什么 Person* arr2 可以用 arr2[0]

1. 核心等价公式(C++ 铁律)

cpp 复制代码
p[i]  <==>  *(p + i)

只要是指针,指向连续同类型内存,都可以用 [下标] 写法

2. 逐层推导

  • arr2 类型:Person* (指向数组首元素的指针)
  • arr2 + 0:还是 Person* 指针
  • *(arr2 + 0)解引用 ,拿到实体对象 Person
  • arr2[0] 等价于 *(arr2+0) → 本身就是对象

3. 关键区分

  • arr2 + i → 指针 Person*
  • arr2[i] → 实体对象 Person

三、为什么 arr2[0] 是对象,不是指针

  1. arr2Person*,存的是首元素地址
  2. arr2[0] 自带隐式解引用 *
  3. 解引用后,访问到内存里真实的 Person 实例
  4. 类型从 Person* 变成 Person

通俗比喻:

arr2 是手指(指针)

arr2+0 还是手指

arr2[0] 顺着手指找到真人(实体对象)


四、.-> 运算符终极规则

1. 使用铁律

  • 实体对象 / 引用 → 用 .
  • 对象指针 → 用 ->

2. 等价对照

cpp 复制代码
// 指针 -> 用 ->
(arr2 + 0)->showPerson();

// 解引用得到对象 -> 用 .
(*arr2).showPerson();
arr2[0].showPerson();
(*(arr2 + 0)).showPerson();

3. 错误写法

cpp 复制代码
(*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. 记忆口诀

  • 括号包星:数组指针
  • 无括号先数组:指针数组

六、关键类型汇总

表达式 类型 用法
arr2 Person* 指针
arr2+0 Person* 指针,用 ->
*arr2 Person 对象,用 .
arr2[0] Person 对象,用 .

七、一句话终极总结

  1. 指针可以用 []arr2[i] = *(arr2+i)
  2. 带下标 [] 就是隐式解引用 ,得到实体对象,用 .
  3. 不带下标、变量+i 是指针,用 ->
  4. 无括号是指针数组,带括号包星是数组指针
  5. Person* arr2 只是普通首元素指针,不属于上面两者
相关推荐
计算机安禾1 小时前
【c++面向对象编程】第4篇:类与对象(三):拷贝构造函数与深浅拷贝问题
开发语言·c++·算法
j_xxx404_1 小时前
Linux共享内存原理与实战:从内核到C++实现|附源码
linux·运维·开发语言·c++·人工智能
计算机安禾1 小时前
【c++面向对象编程】第1篇:从C到C++:面向对象编程思想入门
c语言·c++·算法
leo__5201 小时前
基于 OpenCV + Qt 的水果智能识别分类系统
qt·opencv·分类
liuhuizuikeai1 小时前
菜品抽奖活动MFC+服务端
c++·windows·mfc
ouliten1 小时前
C++笔记:Lambda表达式
c++·笔记
minji...1 小时前
Linux 网络基础(五)守护进程化,前后台进程组,作业,会话,setsid(),daemon(),端口号频繁更换问题
linux·运维·服务器·网络·c++·tcp/ip
Brilliantwxx1 小时前
【算法题】递归树+哈希表+分治异或+双指针
开发语言·c++·笔记·算法
Hello:CodeWorld1 小时前
高性能多线程数据采集与持久化方案设计与实现
开发语言·c++