Momenta C++ 智驾 一面 面经

1. 自我介绍

2. vector 的底层实现原理是什么?扩容时发生了什么?

答:vector 本质是一块连续的堆内存,维护三个指针:指向数据起始位置、指向当前末尾、指向分配内存的末尾,分别对应 sizecapacity

扩容过程:

  • size == capacity 时,继续 push_back 会触发扩容
  • 申请一块新的更大的内存(通常是原来的 1.5 倍或 2 倍,不同实现不同)
  • 把原来的元素全部移动(或复制)到新内存
  • 释放旧内存
  • 更新内部指针

关键点:

  • 扩容后所有迭代器、指针、引用全部失效,因为底层内存地址变了
  • 扩容是 O(n) 操作,但均摊下来每次 push_back 是 O(1)
  • 如果提前知道大小,用 reserve 预分配,避免多次扩容
  • shrink_to_fit 可以释放多余容量,但不保证一定生效

3. newmalloc 的区别是什么?deletefree 能混用吗?

答:区别:

类型

C 标准库函数

C++ 运算符

返回值

void*

,需要强转

对应类型的指针

构造函数

不调用

调用

失败处理

返回

NULL

抛出

std::bad_alloc

大小

手动指定字节数

自动计算

不能混用:

  • malloc 分配的内存用 delete 释放:未定义行为,delete 会尝试调用析构函数,但对象从未被构造过
  • new 分配的内存用 free 释放:析构函数不会被调用,可能导致资源泄漏,内存管理器的元数据也可能不兼容
  • new[] 必须用 delete[]new 必须用 delete,混用同样是未定义行为

4. 说一下你理解的 RAII,结合智能指针讲一下

答:RAII(Resource Acquisition Is Initialization):资源的生命周期绑定到对象的生命周期,在构造函数里获取资源,在析构函数里释放资源,利用 C++ 对象离开作用域自动析构的特性,保证资源不泄漏。

智能指针是 RAII 的典型应用:

unique_ptr

  • 独占所有权,同一时刻只有一个 unique_ptr 指向某个对象
  • 不可复制,只能移动(std::move
  • 零开销,和裸指针性能相同
  • 适合明确的单一所有权场景

shared_ptr

  • 共享所有权,内部维护引用计数
  • 复制时引用计数加一,析构时减一,减到零时释放资源
  • 有额外的控制块内存开销和引用计数的原子操作开销
  • 注意循环引用问题:A 持有 B 的 shared_ptr,B 持有 A 的 shared_ptr,两者引用计数永远不为零,内存泄漏

weak_ptr

  • 不增加引用计数,用于打破循环引用
  • 使用前需要 lock() 升级为 shared_ptr,如果对象已销毁则返回空

5. 多态是怎么实现的?虚函数表的结构是什么样的?

答:C++ 多态通过虚函数表(vtable)实现:

  • 每个含有虚函数的类,编译器为其生成一张虚函数表,表里存放该类所有虚函数的函数指针
  • 每个该类的对象,在内存布局的最开头有一个隐藏的虚指针(vptr),指向该类的虚函数表
  • 调用虚函数时,通过对象的 vptr 找到虚函数表,再通过函数在表中的偏移找到实际函数地址,完成调用

派生类的情况:

  • 派生类有自己的虚函数表
  • 如果重写了基类的虚函数,表中对应位置替换为派生类的函数指针
  • 如果没有重写,继承基类的函数指针
  • 派生类新增的虚函数追加在表的末尾

性能影响:

  • 虚函数调用比普通函数多一次间接寻址(通过 vptr 找表,再找函数地址)
  • 无法内联,因为编译期不知道实际调用哪个函数
  • 在自动驾驶等对性能敏感的场景,热路径上要谨慎使用虚函数

6. std::move 做了什么?移动语义解决了什么问题?

答:std::move 本身不移动任何东西,它只是一个类型转换,把左值强制转换为右值引用,告诉编译器"这个对象可以被移动"。

移动语义解决的问题:

  • 在 C++11 之前,对象传递和返回都是复制,对于持有大量资源的对象(比如 vectorstring)代价很高
  • 移动语义允许"偷走"源对象的资源,而不是复制一份,源对象变成一个有效但未指定状态(通常是空)

移动构造函数和移动赋值运算符:

// 移动构造:把 other 的内部指针偷过来,other 置空
MyClass(MyClass&& other) noexcept
    : data(other.data), size(other.size) {
    other.data = nullptr;
    other.size = 0;
}

实际场景:

  • 函数返回局部对象时,编译器会优先使用 RVO(返回值优化),其次使用移动语义,避免不必要的复制
  • 把对象放入容器时,如果对象是临时值,用移动而不是复制
  • std::move 用完之后不要再使用被移动的对象,状态未定义

7. 讲一下 const 的几种用法,const 成员函数里能修改成员变量吗?

答:const 的用法:

  • const int a:常量,不可修改
  • const int* p:指向常量的指针,不能通过 p 修改值,但 p 本身可以指向别处
  • int* const p:常量指针,p 不能指向别处,但可以通过 p 修改值
  • const int* const p:两者都不能改
  • const 成员函数:函数声明末尾加 const,承诺不修改对象的成员变量,this 指针变为 const T*

const 成员函数里修改成员变量:

  • 普通成员变量:不能修改,编译报错
  • mutable 修饰的成员变量:可以修改,mutable 专门用于在 const 函数里需要修改的场景,比如缓存、互斥锁、引用计数
  • 通过 const_cast 去掉 const 强行修改:未定义行为,不要这样做

8. 进程和线程的区别?线程间同步有哪些方式?

答:区别:

  • 进程是资源分配的基本单位,有独立的地址空间、文件描述符、信号处理等
  • 线程是 CPU 调度的基本单位,同一进程内的线程共享地址空间和大部分资源,但有独立的栈、寄存器、线程局部存储
  • 进程间切换开销大(需要切换页表等),线程间切换开销小
  • 进程间通信需要专门的 IPC 机制,线程间可以直接读写共享内存,但需要同步

线程间同步方式:

互斥锁(mutex):保护临界区,同一时刻只有一个线程进入

std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); // RAII,自动释放

条件变量(condition_variable):线程等待某个条件成立

std::condition_variable cv;
cv.wait(lock, []{ return ready; }); // 等待
cv.notify_one(); // 通知

原子操作(atomic):对简单类型的无锁操作,比互斥锁轻量

std::atomic<int> counter{0};
counter.fetch_add(1); // 原子加一

读写锁(shared_mutex):允许多个读者同时读,写者独占

信号量(C++20 的 std::semaphore):控制并发访问数量

9. 内存泄漏怎么排查?用过哪些工具?

答:排查思路:

代码层面:

  • 检查每个 new 是否有对应的 delete,优先用智能指针代替裸指针
  • 检查异常路径,异常抛出时是否跳过了 delete
  • 检查循环引用,shared_ptr 互相持有
  • 检查容器里存放的裸指针,容器清空时是否释放了指针指向的内存

工具:

Valgrind(Linux):

  • valgrind --leak-check=full ./program
  • 能检测内存泄漏、越界访

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

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

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

全部评论
可以的,写的很好呢
点赞 回复 分享
发布于 03-29 21:49 北京

相关推荐

昨天 12:53
门头沟学院 安卓
发个面经积攒人品。1.&nbsp;&nbsp;(开场)请做一个简单的自我介绍。2.&nbsp;&nbsp;(算法题)实现一个时间复杂度最低的排序算法(给定正整数且已知最大值)。3.&nbsp;&nbsp;(Java基础)Java的基本数据类型有哪些?4.&nbsp;&nbsp;(Java基础)byte类型的取值范围是多少?5.&nbsp;&nbsp;(Java基础)int占几个字节?6.&nbsp;&nbsp;(Java基础)你知道Java的拆箱和装箱吗?7.&nbsp;&nbsp;(Java基础)拆箱和装箱会带来什么问题?8.&nbsp;&nbsp;(Java基础)它(指Integer的缓存机制)一定会有拆箱和装箱的开销吗?9.&nbsp;&nbsp;(Java集合)Java里面的集合类分为哪些类型?10.&nbsp;(Java集合)List里面的ArrayList和LinkedList有什么区别?11.&nbsp;(Java集合)为什么会有这个区别?(指上一个问题中两个List的不同特性)12.&nbsp;(Java集合)ArrayList扩容是怎么扩的?13.&nbsp;(Java集合)为什么(ArrayList)扩容1.5倍?14.&nbsp;(Java集合)HashMap你了解吗?它是什么结构?15.&nbsp;(数据结构)为什么红黑树的查询性能(比链表)好?16.&nbsp;(数据结构)二叉(搜索)树的查询效率一定是O(log&nbsp;n)吗?17.&nbsp;(数据结构)那为什么不做一个完全平衡的(AVL)二叉树?18.&nbsp;(Java集合)HashMap的哈希算法是怎么样的?19.&nbsp;(Java并发)Java里面多线程编程,为了保证线程安全,有哪些技术?20.&nbsp;(Java并发)刚刚说的Atomic是怎么去实现线程安全的?21.&nbsp;(Java集合)HashMap是线程安全的吗?22.&nbsp;(Java集合)有哪些线程安全的Map实现?23.&nbsp;(Java并发)ConcurrentHashMap是怎么去实现(线程安全)的?24.&nbsp;(Android)安卓里面的Handler机制你了解吗?25.&nbsp;(Android)你刚刚提到的epoll机制,它是什么样的(通信机制)?26.&nbsp;(Android)安卓的那个RecycleView你了解过吗?27.&nbsp;(Android)安卓里面进程间通信的方式(有哪些)?28.&nbsp;(Android)你有了解Binder机制吗?29.&nbsp;(框架)看你简历上有提到Retrofit框架,你用过这个框架吗?30.&nbsp;(框架)Retrofit的框架是怎么去实现它接口调用的(机制)?31.&nbsp;(反问前)你那边有问题要问我吗?
查看30道真题和解析
点赞 评论 收藏
分享
昨天 12:56
门头沟学院 安卓
多发面经,积攒人品。许愿🙏云智二面通过。1.&nbsp;&nbsp;然后你是一个人开发了服务端和客户端吗?2.&nbsp;&nbsp;那您能讲一下整体的一个设计,或者是说…您能讲一下整一个流程吗?整个系统,比如说你打开App的时候,它现在具备的一个功能是进入房间,然后去跟其他人去进行一个会议通信是吧?3.&nbsp;&nbsp;你这个是有部署到云端吗?还是其实本身在局域网?4.&nbsp;&nbsp;你现在有可演示的Demo吗?5.&nbsp;&nbsp;那你讲一下你安卓整体工程的一个结构,然后分别它的一些逻辑、设计等等。6.&nbsp;&nbsp;你提这个entity是你跟前后台通信的协议是吧?7.&nbsp;&nbsp;那你这个东西序列化它的一个作用是什么?为什么需要序列化?8.&nbsp;&nbsp;那你上面不是用GSON吗?9.&nbsp;&nbsp;你的服务端也是Java写的吗?10.&nbsp;如果说你要去做跨平台,或者你的客户端是C++的,那它收到之后,它C++那边能反序列化吗?11.&nbsp;那用JSON其实上也是可以跨平台的是吧?那你这里是不是直接用JSON就跨平台就行了?12.&nbsp;这个Fragment跟Activity有什么区别?13.&nbsp;那它什么情况下会需要使用Fragment而不直接用Activity?14.&nbsp;想一下为什么使用Retrofit?用OKHttp,或者直接用Java自带的Http请求类实现可以吗?它们之间你觉得差异是什么?15.&nbsp;那你能顺带讲一下HTTP协议的几个版本的区别吗?16.&nbsp;那你这个整体的一个架构是重要的整体的一个架构吗?(追问项目架构)17.&nbsp;那你也想一下这种MVP/MVVM架构的一个优势和好处,它解决什么问题?18.&nbsp;那ViewModel就是它怎么可以通过什么方式去驱动UI的更新?19.&nbsp;你这里涉及到多线程吗?你这里的网络请求是在什么线程?20.&nbsp;IO线程可以更新UI吗?为什么IO线程不能更新UI?21.&nbsp;那你平时做多线程的时候怎么去解决这种线程安全问题?就是线程去修改这种共享变量的安全问题。22.&nbsp;协程怎么切换不同的线程?23.&nbsp;你自己为什么用WebSocket了?不是直接用TCP?WebSocket跟TCP有啥区别?24.&nbsp;你这个视频渲染组件,它是一个View吧?为什么把一个View放在这个层级,是不太合理吧?25.&nbsp;你觉得应该怎么放?放在哪里比较合适?你这个分层就不合理。26.&nbsp;你怎么样知道怎么去实现一个自定义View吗?有做过吗?27.&nbsp;你了解事件分发的一个流程吗?
查看27道真题和解析
点赞 评论 收藏
分享
04-13 09:24
已编辑
电子科技大学 C++
自我介绍&nbsp;实习1.&nbsp;实习为什么离职?2.&nbsp;Unittest&nbsp;的框架是&nbsp;Google&nbsp;还是自研?八股1.&nbsp;多线程的要点?2.&nbsp;死锁如何解决?如何避免?3.&nbsp;线程通信方式?4.&nbsp;进程通信方式?5.&nbsp;管道的底层实现?6.&nbsp;讲一下&nbsp;GDB7.&nbsp;C++类型转换8.&nbsp;dynamic_cast&nbsp;和&nbsp;static_cast&nbsp;使用场景9.&nbsp;C++&nbsp;struct&nbsp;和&nbsp;class&nbsp;的区别&nbsp;ANS10.&nbsp;C++&nbsp;vector&nbsp;和&nbsp;C&nbsp;数组的区别&nbsp;REF11.&nbsp;vector&nbsp;底层扩容实现12.&nbsp;如何减少&nbsp;vector&nbsp;拷贝花销?初始化花销?13.&nbsp;如何减少&nbsp;vector&nbsp;一次性存入太多元素的内存花销(emplace_back&nbsp;和&nbsp;push__back)14.&nbsp;讲讲&nbsp;C++移动语义&nbsp;move?15.&nbsp;静态绑定和动态绑定的区别?16.&nbsp;讲一下多态(静态多态、动态多态)17.&nbsp;讲一下纯虚函数18.&nbsp;为什么类里面包含虚函数,那么析构函数必须是虚函数?19.&nbsp;如何避免资源泄漏?20.&nbsp;shared_ptr&nbsp;实现方式21.&nbsp;用过哪些设计模式?闲聊1.&nbsp;遇到最困难的点,怎么发现并解决?2.&nbsp;反问无手撕总结自我介绍得实习部分讲太久了,后面基本没问了,项目也是一点点都没问。八股被问蒙蔽了,很像高三的时候知识点在脑子深处就是找不到的感觉,有几题大脑直接空白了。也好,再准备准备,感觉一面纯拷打八股基础知识,二面才会问项目,当然也不知道有没有二面了。
查看25道真题和解析
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

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