小马智行C++ 一面
1. C++ 中左值、右值、右值引用分别是什么?
- 左值一般表示有名字、可取地址、生命周期相对明确的对象;右值通常是临时对象、字面量,或者表达式计算后的中间结果。
- 右值引用本质上是为了高效接管临时对象资源,避免不必要的深拷贝,这也是移动语义成立的基础。
std::move本身不移动对象,它只是把对象强制转换成右值引用,真正是否发生资源转移取决于移动构造或移动赋值是否实现。- 在工程里,右值引用常用于容器扩容、返回值优化配合、资源句柄转移等性能敏感场景。
2. 什么情况下需要把析构函数写成虚函数?
- 只要一个类可能被当作基类使用,并且会通过基类指针删除派生类对象,就必须把析构函数声明为虚函数。
- 如果基类析构不是虚函数,
delete basePtr时只会调用基类析构,派生类资源无法正确释放,容易造成资源泄漏。 - 虚析构函数保证对象销毁时会按派生类到基类的顺序完整调用析构链。
- 如果一个类明确不会被继承,或者不会通过基类指针释放对象,那么不一定需要虚析构。
3. 深拷贝和浅拷贝有什么区别?
- 浅拷贝只是复制对象成员的值,如果成员里有指针,那么复制后两个对象可能指向同一块堆内存。
- 深拷贝会额外申请新资源,把原对象内容完整复制过去,这样两个对象彼此独立,互不影响。
- 如果类内部管理动态资源,只依赖编译器生成的默认拷贝构造和赋值运算符,往往会引发重复释放、悬空指针等问题。
- 这类场景通常要自己实现拷贝控制,或者直接使用
std::string、std::vector、智能指针等 RAII 类型来规避手写资源管理。
4. new 和 malloc 的区别是什么?
malloc是 C 风格内存分配函数,只负责按字节申请内存,不会调用构造函数;new是 C++ 运算符,既分配内存也会执行对象构造。malloc返回void*,需要手动类型转换;new返回对应类型指针,类型更安全。- 释放时也必须成对使用,
malloc/free一组,new/delete一组,混用会导致未定义行为。 - 对于对象创建,尤其是有构造、析构逻辑的类,优先使用
new或更现代的智能指针和容器。
5. const 可以修饰哪些位置?分别有什么意义?
- 修饰变量时,表示该变量初始化后不能被修改,例如
const int a = 10;。 - 修饰指针时需要区分两种情况:
const int* p表示不能通过p改值;int* const p表示指针自身不能改指向。 - 修饰成员函数时,表示该函数不会修改对象的非
mutable成员,因此可以被常对象调用。 - 在接口设计中,合理使用
const能提高可读性,明确只读语义,也有助于编译器做更多检查。
6. vector 扩容时会发生什么?
- 当
vector当前容量不足以容纳新元素时,会重新申请一块更大的连续内存,然后把旧元素搬迁过去。 - 搬迁过程通常优先调用移动构造,如果类型不支持移动才会退化成拷贝构造,因此现代 C++ 类型设计会尽量支持移动语义。
- 扩容后,原来的迭代器、指针、引用大概率都会失效,这是面试和工程里都非常高频的坑点。
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++八股文全集 文章被收录于专栏
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。
字节跳动公司福利 1471人发布
查看14道真题和解析