C++ 面试问题
内容提要
这篇文章是关于C++面试问题的后续,介绍了局部变量、delete和delete[]的区别、赋值运算符的实现、std::map和std::unordered_map的区别、虚函数的实现以及错误的修复方法。这些问题适用于C++编程职位的面试。
延伸解读
面试考察的深度层次
文章将问题分为普通和高级(senior)两类,例如赋值运算符的异常安全、构造函数异常导致的内存泄漏、placement new 的限制等仅针对高级候选人。这提示面试官可以根据职位级别选择问题,初级者需掌握基础概念如局部变量生命周期、delete 与 delete[] 区别,而高级者应能分析异常安全、资源管理陷阱及底层实现机制。
资源管理:从手动到智能指针
文章多处强调手动管理内存的风险:赋值运算符需处理自赋值和深拷贝,否则导致内存泄漏或双重删除;构造函数中若成员初始化抛出异常,已构造的成员不会自动清理,造成泄漏。解决方案是使用 std::unique_ptr,它能在异常发生时自动释放已完全构造的成员。这反映了现代 C++ 推荐 RAII 和智能指针来避免资源泄漏。
容器选择:map 与 unordered_map 的权衡
std::map 基于平衡二叉树,插入、查找、删除的平均和最坏时间复杂度均为 O(lg n),且迭代时元素有序;std::unordered_map 基于哈希表,平均 O(1),但最坏 O(n),迭代无序。通常优先选择 unordered_map 以获得平均常数时间,但若需要有序遍历,则必须使用 map,且要求元素可比较(less-than comparable)。
虚函数实现与运行时多态
C++ 编译器通常通过虚表(vtbl)和虚指针(vptr)实现虚函数。每个含虚函数的类有一个 vtbl,存放各虚函数(包括析构函数)的指针,指向该类覆盖的实现;每个对象内含一个 vptr 指向所属类的 vtbl。运行时根据 vptr 确定对象实际类型,从而调用正确的函数,实现多态。
Q&A
局部变量在函数返回后会发生什么?
局部变量在函数返回后会消失,导致引用成为悬空引用。
delete和delete[]有什么区别?
delete用于销毁单个对象,而delete[]用于销毁对象数组。
如何实现赋值运算符以避免内存泄漏?
需要实现深拷贝,确保在赋值前删除已有指针,并分配新的内存。
std::map和std::unordered_map的时间复杂度有什么不同?
std::map的时间复杂度为O(lg n),而std::unordered_map的平均时间复杂度为O(1)。
虚函数是如何在C++中实现的?
虚函数通过虚表(vtbl)和虚指针(vptr)实现,确保运行时的多态性。
placement new的用途是什么?
placement new用于在特定内存地址创建对象。