1.weak_ptr解决lambda捕获this导致循环引用
📌 2. Lambda 表达式中的 weak_ptr 解决方案
❌ 问题:Lambda 捕获 this 导致循环引用
class MyClass {
public:
void start() {
// 错误:捕获 this 会导致循环引用
timer_.async_wait([this](auto...) {
// 处理逻辑
});
}
private:
boost::asio::steady_timer timer_;
};
✅ 正确方案:用 weak_ptr 代替 this
class MyClass {
public:
void start() {
// 创建 weak_ptr 用于安全捕获
auto self = std::weak_ptr<MyClass>(shared_from_this());
timer_.async_wait([self](auto...) {
if (auto ptr = self.lock()) {
// 安全访问 ptr
}
});
}
private:
boost::asio::steady_timer timer_;
};
💡 为什么有效 :
weak_ptr::lock()返回shared_ptr,如果对象已销毁则返回空指针,避免访问已释放内存。
2.线程,进程创建方法
Linux 创建进程/线程
🐳 创建进程
pid_t pid = fork(); // 复制当前进程
if (pid == 0) {
// 子进程
} else {
// 父进程
}
🐤 创建线程
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
💻 C++ 创建线程
#include <thread>
void thread_func() { /* ... */ }
std::thread t(thread_func);
t.join(); // 等待线程结束
💡 为什么 C++ 用
std::thread?跨平台、RAII 安全(自动管理线程生命周期)。
3.TCP socket 编程流程
📡 服务端:
int sock = socket(AF_INET, SOCK_STREAM, 0);
bind(sock, ...);
listen(sock, 5);
int conn = accept(sock, ...);
recv(conn, ...);
send(conn, ...);
close(conn);
close(sock);
📡 客户端:
int sock = socket(AF_INET, SOCK_STREAM, 0);
connect(sock, ...);
send(sock, ...);
recv(sock, ...);
close(sock);
💡 关键点:
- 服务端:
bind→listen→accept- 客户端:
connect→ 通信
4.weak_ptr除了循环引用,还解决了哪些问题。
-
资源存在性检测 (最实用的功能!) weak_ptr能让你在访问对象前先检查一下:"嘿,这个对象还在吗?"这通过
expired()方法实现。比如在事件系统中,你可以在触发回调前先检查对象是否还活着,避免访问已经销毁的对象。 -
避免悬空指针 (C++开发者最头疼的问题之一) 通过
lock()方法,weak_ptr能安全地转换成shared_ptr,如果对象已经销毁,lock()会返回空指针。这样就能避免"使用已释放内存"的未定义行为,就像给你的代码加了个安全气囊。 -
提供非拥有式观察机制(这才是weak_ptr的精髓!) 它不增加引用计数,只是"安静地观察"对象状态,不会影响对象的生命周期。这在很多场景下特别有用:
- 缓存系统:缓存对象引用但不阻止对象被销毁
- 观察者模式:主题可以弱引用观察者,避免观察者与主题间的循环引用
- 树形结构:子节点弱引用父节点,避免父子节点相互持有导致的内存泄漏
5.gcc编译的时候,可执行程序崩溃了,应该怎么查找崩溃的原因? 编译成功,但是运行时崩溃了?
-
看错误信息:
- Linux:
Segmentation fault (core dumped)或Bus error - Windows:
0xC0000005(访问违规) 或0xC0000279(未知错误)
- Linux:
-
获取更多信息:
- Linux:
dmesg -T | tail看内核日志 - Windows:查看"事件查看器"中的应用程序错误
- Linux:
🔍 排查步骤(从易到难)
✅ 第一步:检查编译警告(90%的问题都藏在这里!)
gcc -Wall -Wextra -Werror your_program.c -o your_program
-Wall:显示所有警告-Wextra:显示额外警告-Werror:把警告当错误处理
💡 知识库[7]提到,很多崩溃其实是"函数没有return"导致的,编译器会警告但不会报错,加上-Werror就能提前发现。
🧪 第二步:用GDB调试(最直接有效的方法!)
gdb ./your_program
(gdb) run
# 程序崩溃后
(gdb) backtrace # 查看调用栈
(gdb) print variable_name # 查看变量值
(gdb) list # 查看崩溃附近的代码
💡 知识库[9]提到,GDB是排查运行时崩溃的神器,能精确找到崩溃位置。
🧪 第三步:内存错误检测(Valgrind)
valgrind --tool=memcheck --leak-check=full ./your_program
Valgrind会报告:
- 无效的内存访问(如野指针)
- 内存泄漏
- 数组越界
💡 知识库[8]提到,90%的运行时崩溃都是内存问题,Valgrind能帮你精准定位。
🔍 第四步:检查常见崩溃原因
| 崩溃类型 | 常见原因 | 解决方法 |
|---|---|---|
| 段错误 | 空指针解引用、数组越界、使用已释放内存 | 检查指针初始化、添加边界检查、释放后置空 |
| 未定义引用 | 链接时缺少库 | 添加-l库名,如-lm链接数学库 |
| 优化导致崩溃 | 编译优化选项问题 | 尝试-O0编译,排除优化问题 |
| 依赖库问题 | DLL/动态库缺失或版本不匹配 | 检查ldd(Linux)或依赖库安装 |
💡 知识库[5]提到,我曾经遇到过一个
-O3优化导致的崩溃,用-fno-tree-loop-vectorize就解决了。
🧪 第五步:简化问题(快速定位)
- 写个最小可复现代码(最小测试用例)
- 逐步添加代码,定位问题点
- 用
printf在关键位置输出,确认执行流程
💡 知识库[3]提到,C语言"代码正确却无法运行"的常见原因,很多都是因为编译检查的局限性,需要运行时验证。
🌟 实用技巧
-
在关键位置加日志:
std::cout << "Before malloc: " << ptr << std::endl; ptr = malloc(100); std::cout << "After malloc: " << ptr << std::endl; -
检查编译器版本:
gcc --version- 知识库[11]提到,不同版本GCC对代码处理可能不同
-
检查系统环境:
- Linux:
ldd ./your_program查看依赖库 - Windows:确保安装了VC++运行库
- Linux:
6.http的get和post有什么区别吗?
GET是"只读小能手",适合"查资料";POST是"提交小能手",适合"交作业"。
| 特性 | GET | POST | 通俗解释 |
|---|---|---|---|
| 数据位置 | URL后面(?key=value) | 请求体中 | GET:参数在地址栏,谁都能看到 POST:参数藏在"信封"里,只有服务器能看 |
| 数据长度 | 受限(2KB~8KB) | 无限制 | GET:只能写"小纸条" POST:能寄"大包裹" |
| 安全性 | 低(参数暴露在URL) | 较高(参数在Body) | GET:像在公开场合说密码 POST:像在私密房间说密码(但还是要HTTPS加密!) |
| 幂等性 | 幂等(多次请求结果相同) | 非幂等(可能重复创建资源) | GET:刷新页面,结果一样 POST:刷新支付页面,可能多扣钱 |
| 缓存 | 可缓存 | 不可缓存 | GET:浏览器会记住,下次直接用 POST:每次都要重新提交 |
| 书签 | 可保存为书签 | 不可保存为书签 | GET:能收藏喜欢的搜索结果 POST:不能收藏"提交按钮" |
| TCP包数量 | 1个 | 2个(有时) | GET:一次搞定 POST:先问"能发吗?",再发内容 |
| 适用场景 | 查询、获取数据 | 提交数据、创建资源 | GET:搜索"手机",看商品详情 POST:登录、注册、提交订单 |