动态内存分配new/delete:面试官问我“new失败会怎样“,我答错了
上篇聊了指针运算和数组的关系有人后台说想看new/delete。来了。这个话题在面试里考得特别频繁特别是机器人方向——你写驱动、处理传感器数据、管理点云……哪一样离得开动态内存讲个面试故事。去年面试一个候选人简历上写着熟悉C内存管理。我问他new和malloc有什么区别他说new是C的malloc是C的。new会调用构造函数malloc不会。我说对。然后追问new失败会怎样他想了一下说返回NULL我说你确定他犹豫了改口说返回NULL或者抛出异常我告诉他new失败默认抛出std::bad_alloc异常不会返回NULL。除非你用new(nothrow)。他愣了一下说这个我还真不知道。讲真这种问题我见过太多次了。很多人用了好几年new/delete从来没关注过失败处理。但在面试里这种细节就是区分度。new/delete到底干了什么先搞清楚底层机制。new做两件事分配内存和调用构造函数。MyClass* obj new MyClass(10);这一行代码编译器帮你做了两步调用operator new分配足够大的内存在这块内存上调用MyClass的构造函数delete也做两件事调用析构函数和释放内存。delete obj;先调用obj的析构函数然后调用operator delete释放内存。而malloc/free只管分配和释放内存不管构造和析构。这就是new/delete和malloc/free最本质的区别。在机器人开发里你经常要创建传感器对象、初始化控制器这些都涉及构造函数。如果你用malloc分配内存构造函数不会被调用对象就是半成品用起来会出各种奇怪的bug。new失败会怎样三种处理方式回到开头那个面试问题。new失败比如内存不够了默认会抛出std::bad_alloc异常。如果你没catch程序直接崩。try { int* p new int[1000000000]; // 申请4GB } catch (const std::bad_alloc e) { cout 内存分配失败 e.what() endl; }但很多时候你不想用异常来处理。C提供了另一种方式new(nothrow)。#include new int* p new(nothrow) int[1000000000]; if (p nullptr) { cout 内存分配失败 endl; }new(nothrow)失败时不抛异常而是返回nullptr。这种写法更接近C的风格但在嵌入式系统里很常见——因为很多嵌入式环境不支持异常。还有一种方式自定义new_handler。当new失败时系统会调用你设置的handler函数void myNewHandler() { cout 内存不够了尝试释放一些缓存... endl; // 释放缓存然后重试 } std::set_new_handler(myNewHandler); int* p new int[1000000000];面试的时候如果你能说出这三种处理方式面试官会觉得你对内存管理有深入理解。数组的new/delete最容易写错的地方new/delete处理数组时有个特别容易犯的错int* arr new int[100]; delete arr; // 错应该是delete[] arr;delete和delete[]的区别delete只析构一个对象delete[]会析构数组里的每个对象。对于基本类型int、float两者效果一样因为没有析构函数。但对于自定义类型差别就大了class Sensor { public: ~Sensor() { /* 释放硬件资源 */ } }; Sensor* sensors new Sensor[10]; delete sensors; // 只析构sensors[0]其余9个没析构 delete[] sensors; // 正确10个都析构了如果忘了加[]9个Sensor对象的析构函数不会被调用硬件资源就泄漏了。在机器人开发里这种错误特别容易出在传感器阵列、关节控制器数组这类场景。我见过一个真实的case一个同事new了一个电机控制器数组delete的时候忘了加[]结果9个电机的通信端口没关闭程序重启后端口被占用电机全挂了。new/delete和malloc/free能混用吗不能。new分配的内存必须用delete释放malloc分配的内存必须用free释放。混用会导致未定义行为。int* p1 new int(10); free(p1); // 未定义行为 int* p2 (int*)malloc(sizeof(int)); delete p2; // 未定义行为为什么因为new和malloc可能用不同的内存管理策略。new在分配内存时可能会额外记录一些信息比如对象大小delete依赖这些信息来正确释放。free不认识这些信息就会出错。在机器人开发里如果你要对接C语言的库比如某些传感器驱动只提供了C接口要注意内存管理的一致性。C库用malloc分配的内存在C里用delete释放是新手常犯的错误。在机器人开发里什么时候用new/delete现代C推荐用智能指针和容器来管理内存尽量避免裸的new/delete。但在某些场景下你还是需要用场景一对接C接口。某些硬件驱动只提供了C接口你需要手动管理内存。场景二性能敏感的场景。智能指针有引用计数的开销在高频调用的控制循环里你可能需要用裸指针来避免这个开销。场景三自定义内存池。后面会专门讲但核心思路就是自己管理一块内存用new/delete的方式分配释放。不过日常开发中我强烈建议用std::vector、std::unique_ptr、std::shared_ptr来代替裸的new/delete。安全、方便、不容易出错。补充一个面试加分点placement new。它允许你在已经分配好的内存上直接构造对象不再向系统申请新内存。语法是new (address) MyClass(args)。在机器人开发里内存池和嵌入式系统经常用到这个技巧。比如你先分配一块大内存然后用placement new在上面逐个构造对象避免了反复向系统申请内存的开销。面试时能聊到这个面试官会觉得你对内存管理的理解不止停留在表面。再补充一个高频面试追问operator new和operator delete可以重载吗答案是可以的。你可以为某个类自定义内存分配策略比如从预分配的内存池里取内存而不是走系统的堆分配。这在机器人实时控制系统里特别有用因为系统malloc的耗时不确定实时任务不能容忍这种不确定性。重载的写法很简单在类里声明static void* operator new(size_t size)和static void operator delete(void* ptr)就行。面试官听到你聊到内存池和实时性这两个关键词基本就知道你有实际工程经验了不是只会背课本知识。给正在准备面试的你一点建议new/delete在面试里考得细。我建议你搞清楚这几个点new/delete底层做了什么分配构造/析构释放、new失败的三种处理方式、数组的delete[]不能忘、new/delete和malloc/free不能混用。面试的时候如果你能把这些点讲清楚还能结合机器人开发中的实际场景举例面试官会觉得你有工程经验。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第12篇 指针运算与数组的关系——面试官最爱挖的底层细节 下一篇预告第14篇 内存泄漏——一个让机器人跑着跑着就崩了的元凶有任何问题欢迎评论区留言我会尽量回复。