尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C++ 内存管理实战:从 5 个经典笔试题看 new/delete 与野指针规避

C++ 内存管理实战:从 5 个经典笔试题看 new/delete 与野指针规避
📅 发布时间:2026/7/13 14:33:48

C++ 内存管理实战:从 5 个经典笔试题看 new/delete 与野指针规避

1. 动态内存分配基础陷阱

案例1:GetMemory函数的内存泄漏

void GetMemory(char *p) { p = (char*)malloc(100); // 问题点:修改的是局部指针副本 } void Test() { char *str = NULL; GetMemory(str); // str仍为NULL strcpy(str, "hello"); // 运行时错误 }

修正方案:二级指针传递

void GetMemory(char **p) { *p = (char*)malloc(100); if (*p == NULL) { // 良好的健壮性检查 perror("malloc failed"); exit(EXIT_FAILURE); } } void Test() { char *str = NULL; GetMemory(&str); strcpy(str, "hello"); free(str); // 必须配套释放 }

关键知识点对比表:

错误类型问题表现解决方案内存示意图
值传递指针无法修改外部指针使用指针的指针[原始栈帧]->[修改副本]
未检查分配可能使用NULL指针添加NULL检查malloc失败返回NULL
忘记释放内存泄漏RAII原则已分配块未回收

2. 返回栈内存引发的野指针

案例2:局部数组返回

char *GetString() { char p[] = "hello world"; // 栈上数组 return p; // 返回局部变量地址 } void Test() { char *str = GetString(); // str成为野指针 printf(str); // 未定义行为 }

修正方案1:静态存储期

char *GetString() { static char p[] = "hello world"; // 静态存储期 return p; // 生命周期持续 }

修正方案2:动态分配

char *GetString() { char *p = (char*)malloc(20); if (p) strcpy(p, "hello world"); return p; // 调用者需负责释放 }

内存生命周期对比:

  • 栈内存:函数返回即失效
  • 静态区:程序运行期间有效
  • 堆内存:需手动管理生命周期

3. delete与delete[]的误用

案例3:混合使用导致的崩溃

class MyClass { public: MyClass() { cout << "ctor" << endl; } ~MyClass() { cout << "dtor" << endl; } }; void Test() { MyClass *p1 = new MyClass[5]; // 调用5次构造函数 MyClass *p2 = new MyClass; delete p1; // 错误!只调用1次析构 delete[] p2; // 错误!错误释放方式 }

修正代码:

void Test() { MyClass *p1 = new MyClass[5]; // new[]分配 MyClass *p2 = new MyClass; // new单对象 delete[] p1; // 正确释放数组 delete p2; // 正确释放单对象 }

底层机制解析:

  1. new[]会在分配的内存块头部存储数组大小(通常在前4字节)
  2. delete[]根据该信息调用对应次数的析构函数
  3. 基本类型无析构函数时,delete和delete[]效果相同但不应混用

4. 多态对象的删除问题

案例4:基类非虚析构

class Base { public: ~Base() { cout << "Base dtor" << endl; } // 非虚析构 }; class Derived : public Base { int *data; public: Derived() : data(new int[100]) {} ~Derived() { delete[] data; cout << "Derived dtor" << endl; } }; void Test() { Base *p = new Derived(); delete p; // 仅调用Base析构,内存泄漏! }

修正方案:虚析构函数

class Base { public: virtual ~Base() { cout << "Base dtor" << endl; } // 虚析构 };

对象销毁过程:

  1. 通过基类指针删除时,虚函数机制确保调用实际类型的析构函数
  2. 析构顺序:派生类->基类
  3. 若基类析构非虚,则只会调用基类析构,导致派生类资源泄漏

5. 野指针检测与防御编程

案例5:重复释放与悬挂指针

void Test() { int *p = new int(10); delete p; // 正确释放 *p = 20; // 使用已释放内存 delete p; // 重复释放 }

防御性编程技巧:

  1. 置空策略:
delete p; p = nullptr; // 确保再次访问会立即崩溃
  1. 智能指针方案:
#include <memory> void SafeTest() { std::unique_ptr<int> p(new int(10)); // 自动管理生命周期 // 无需手动delete }
  1. 内存调试工具:
  • Valgrind:检测内存错误
  • AddressSanitizer:实时内存检查

野指针检测表:

检测方法原理优点限制
置空法释放后立即置NULL简单直接多指针指向时无效
智能指针引用计数/独占所有权自动管理学习曲线稍高
内存标记分配时添加标记可检测越界性能开销
工具检测运行时检查全面检测需要特定环境

6. 现代C++内存管理最佳实践

RAII原则应用:

class ResourceGuard { int *resource; public: explicit ResourceGuard(size_t size) : resource(new int[size]) {} ~ResourceGuard() { delete[] resource; } // 禁用拷贝(C++11风格) ResourceGuard(const ResourceGuard&) = delete; ResourceGuard& operator=(const ResourceGuard&) = delete; // 支持移动(C++11) ResourceGuard(ResourceGuard&& other) noexcept : resource(other.resource) { other.resource = nullptr; } }; void ModernTest() { ResourceGuard guard(100); // 资源获取即初始化 // 无需手动释放,异常安全 }

智能指针对比:

类型所有权线程安全适用场景
unique_ptr独占否单一所有者资源
shared_ptr共享是(控制块)共享所有权
weak_ptr观察是解决循环引用

性能优化技巧:

  1. 内存池技术:
class MemoryPool { struct Block { Block* next; }; Block* freeList; public: void* allocate(size_t size) { if (!freeList) { // 批量分配策略 } void* mem = freeList; freeList = freeList->next; return mem; } };
  1. 对象池模式:
template<typename T> class ObjectPool { std::vector<std::unique_ptr<T>> pool; public: T* acquire() { if (pool.empty()) { return new T(); } auto obj = std::move(pool.back()); pool.pop_back(); return obj.release(); } };

相关新闻

  • 双链表的增操作
  • Laguna-M.1-4bit模型架构深度解析:256专家MoE系统详解
  • 终极指南:如何将Windows电脑变成免费AirPlay接收器

最新新闻

  • 上线后最先崩的不是代码,是小团队盲目追求“全自动化”的幻觉
  • 上海西装定制价值洞察:维纳缇高性价比的六大核心支撑 - 西装爱好者
  • 如何在gemma-4-e4b-it-OptiQ-4bit上实现推测解码:使用助手起草器提升推理速度的完整指南
  • GPT-5.6、Claude、Gemini、DeepSeek 怎么选?按任务选型的终极对比清单
  • Windows系统文件comuid.dll丢失找不到问题解决
  • 热门公告:卡地亚复杂功能腕表维修标准指南 2026 线下服务地址汇总 - 卡地亚售后服务中心

日新闻

  • AI推荐结果怎么优化:适合深圳少儿素质培训机构的GEO服务商哪家好?全程零代码SAAS操作
  • RAG 实战教学完全指南
  • 企业级API网关架构深度解析:IBM Microgateway的技术实现与选型指南

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号