影石 | C++开发工程师 一面 面经

1. 自我介绍

答:按"个人背景 → 核心技术栈 → 代表项目 → 求职意向"四段走,控制在2分钟以内。重点突出和影石岗位相关的经验,比如嵌入式Linux、音视频、IoT方向,不要把简历从头念一遍。结尾说一句为什么对影石感兴趣,体现你做过功课。

2. C++中的深拷贝和浅拷贝有什么区别?什么情况下必须自定义拷贝构造函数?

答:浅拷贝是按位复制,对于含有指针成员的类,两个对象会指向同一块内存,析构时会double free,这是典型的问题场景。深拷贝会为新对象重新分配内存并复制内容,两者完全独立。

必须自定义拷贝构造函数的情况:类中有裸指针管理的堆内存;类持有文件描述符、socket、锁等不可共享的资源;类的语义要求每个对象独立拥有自己的数据。

遵循"三五法则":如果需要自定义析构函数,通常也需要自定义拷贝构造和拷贝赋值;C++11之后还要考虑移动构造和移动赋值,合称"五法则"。现代C++更推荐用RAII封装资源(如unique_ptr),让编译器自动生成正确的拷贝/移动语义,或者显式delete拷贝操作。

3. std::vector的扩容机制是怎样的?频繁push_back导致性能问题时如何优化?

答:vector扩容时,当size等于capacity,会重新分配一块更大的内存(通常是当前capacity的1.5倍或2倍,不同实现不同),把所有元素移动或拷贝到新内存,然后释放旧内存。这个过程时间复杂度是O(n),但均摊下来每次push_back是O(1)。

扩容的代价:内存重新分配、元素移动(如果元素没有noexcept的移动构造,会退化成拷贝)、所有迭代器和指针失效。

优化手段:如果能预估元素数量,提前调用reserve(n)一次性分配足够容量,避免多次扩容;元素类型要提供noexcept的移动构造函数,让vector扩容时用移动而不是拷贝;如果元素是大对象,考虑存指针或用emplace_back直接在容器内构造,避免额外拷贝;如果大小固定,用std::array完全避免动态分配。

4. unordered_map的底层实现是什么?哈希冲突是如何处理的?什么情况下它会退化成O(n)?

答:unordered_map底层是哈希表,用开链法(拉链法)处理冲突,每个桶是一个链表(C++11标准要求),元素根据key的哈希值映射到对应桶,冲突的元素挂在同一个桶的链表上。

查找过程:计算key的hash,取模得到桶索引,遍历该桶的链表逐一比较key,平均O(1),最坏O(n)。

退化成O(n)的情况:哈希函数设计很差,大量key映射到同一个桶,链表变得很长;或者遭受哈希碰撞攻击(恶意构造大量相同哈希的key)。C++标准库对此有一定防护,但自定义哈希函数时要注意。另外,当负载因子(元素数/桶数)超过阈值时会触发rehash,重新分配桶数组并重新插入所有元素,这个过程是O(n)的,会造成偶发性延迟。

和map的选择:map底层红黑树,O(log n)但稳定;unordered_map平均O(1)但有最坏情况,且不保证顺序。需要有序遍历用map,纯查找性能优先用unordered_map,但要用好的哈希函数。

5. 请解释C++中的左值、右值、左值引用、右值引用,以及移动语义解决了什么问题?

答:左值是有名字、有持久地址的表达式,可以出现在赋值号左边;右值是临时的、没有持久地址的表达式,通常是字面量、临时对象、函数返回的非引用值。

左值引用(T&)只能绑定左值;右值引用(T&&)只能绑定右值(临时对象);const左值引用(const T&)可以绑定任何值,这是C++11之前延长临时对象生命周期的唯一手段。

移动语义解决的核心问题:C++11之前,函数返回大对象、容器插入元素,都会触发深拷贝,代价高昂。移动语义允许"窃取"临时对象的资源(比如直接接管其内部指针),而不是复制,原对象置为有效但未定义的状态。典型例子是std::string或std::vector的移动构造,只需要复制几个指针,O(1)完成,而拷贝构造是O(n)。

实际意义:函数返回局部vector,编译器会用RVO(返回值优化)或移动语义,不会有拷贝;往容器里插入临时对象,用emplace_back或传右值,触发移动而不是拷贝。

6. Linux中的管道、消息队列、共享内存三种IPC方式各有什么特点?在嵌入式项目中你会怎么选择?

答:管道(pipe/fifo)是字节流,有内核缓冲,读写是阻塞的,适合父子进程或有亲缘关系的进程间单向数据流,使用简单,但只能传字节流,没有消息边界,需要自己处理粘包。命名管道(FIFO)可以用于无亲缘关系的进程。

消息队列是有消息边界的,每条消息有类型,接收方可以按类型选择性接收,内核维护队列,进程崩溃后消息不丢失(除非系统重启)。适合需要消息分类、异步解耦的场景,但有消息大小限制,吞吐量不如共享内存。

共享内存是最快的IPC方式,多个进程映射同一块物理内存,读写直接操作内存,没有内核拷贝开销。但本身没有同步机制,需要配合信号量或互斥锁使用,编程复杂度最高,出错风险也最大。

嵌入式项目选择思路:数据量小、逻辑简单用管道;需要消息分类、解耦用消息队列;高频大数据量传输(比如视频帧在进程间传递)用共享内存+信号量。影石的全景相机场景,多个进程处理视频流,共享内存是最合适的,避免帧数据在进程间反复拷贝。

7. 什么是内存泄漏?除了忘记delete,还有哪些不容易发现的内存泄漏场景?

答:内存泄漏是申请的堆内存没有被释放,且没有任何指针指向它,导致这块内存永远无法被回收,进程内存持续增长。

容易被忽视的泄漏场景:

shared_ptr循环引用,A持有B的shared_ptr,B持有A的shared_ptr,引用计数永远不为0,两者都不会析构,这是C++项目里最常见的隐性泄漏。

基类析构函数不是虚函数,用基类指针delet

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论
可以的,总结的很好
点赞 回复 分享
发布于 04-05 22:07 北京

相关推荐

三月 · 3.14 / 3.21 美团 – 笔试· 3.15 拼多多笔试挂· 3.17 沐瞳科技测开 一面挂· 3.19 腾讯wxg 一面挂· 3.22 柠檬微趣 笔试挂· 3.24 字节跳动测开 一面挂· 3.25 腾讯云 一面挂· 3.28 阿里集团ai应用笔试 挂· 3.30 美团全栈 一面挂· 3.30 收到腾讯ieg面邀4.1,当时有点崩溃,延迟了一周面试时间,三月惨淡收官四月· 4.3 米哈游一面过,两小时后约二面· 4.7 文远知行 一面挂· 4.7 腾讯ieg 一面过,次日约二面· 4.8 米哈游二面,横向九天挂· 4.8 字节跳动客户端 一面挂· 4.10 美团全栈复活面邀,不匹配拒了· 4.12 鹰角网络 笔试· 4.13 滴滴一面 十分钟后二面 ,临时推迟了影石的面试· 4.13 腾讯ieg二面,次日看官网过· 4.15 影石 移动端 一面· 4.17 美团全栈复活 一面挂· 4.21 腾讯ieg hr面,次日云证· 4.21 滴滴 oc· 4.22 影石 二面· 4.22 吉比特约面拒· 4.22 滴滴offer,腾讯录用评估中· 4.28 腾讯offer三月边实习边面试压力大到头疼,经常崩溃,笔试做的很差,手撕撕不出来,八股常常答不上 ,项目讲不明白。一焦虑就爱吃油炸食品,一周四顿麦当劳,有个周末崩溃的骑了十公里去商场里买舒芙蕾。四月份刚好实习期结束,过清明节调整了状态,给自己做好了心理准备,重新复盘了之前的面试。假期结束后房租也到期了,刚好7.8号有四场面试,就在环球影城旁边订了酒店面试,面完这四场第二天狠狠坐了三趟霸天虎过山车。常常觉得自己准备的不够充分,面经也没有看完,面试之前脑袋空空还很想上厕所,但是实际上一开始自我介绍就冷静了下来,感觉项目能讲的越来越清晰,面试的八股原来来来回回就那么几个常见的,可以熟练的讲清楚了,到后面发现自己就算是完全不会的领域也能冷静的分析并且扯上几句,手撕也能很快做出来了,其实一下场我都不清楚自己是怎么做到的。心态也逐渐从面试很重要一定要过呀转换到熬完这一次就算胜利,哄着自己继续往前走。两个月有种脱胎换骨的感觉,不仅仅在技术层面,这两个月想了很多,想清楚了自己未来想要什么。暑期结束,继续好好学习,好好工作。
点赞 评论 收藏
分享
评论
3
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务