如何通过C++面试:高级问题TOP榜
应聘C++开发岗位的面试始终是一项挑战。如果基础问题(new和malloc的区别、什么是虚析构函数)你很可能已经掌握,那么高级水平则需要深入理解语言的内部机制。在本文中,我们将解析在Middle和Senior职位的技术面试中必然会涉及的关键主题。我们不仅会列出问题,还会提供带有代码示例的详细解答。
1. 智能指针及其内部实现
问题:"请说明std::unique_ptr和std::shared_ptr有何区别?引用计数的线程安全性是如何实现的?"
这是经典问题。面试官希望看到你不仅理解语法,还理解资源所有权的语义。
- std::unique_ptr — 独占所有权。不可复制,只能移动。适用于只有一个所有者的资源。
- std::shared_ptr — 共享所有权。使用引用计数(控制块)。当计数降至零时,资源被销毁。
- std::weak_ptr — "观察者"。不增加引用计数。用于打破
shared_ptr中的循环依赖。
高级细节:shared_ptr中的引用计数是线程安全的(使用原子操作),但这并不使被管理的对象本身变得线程安全。此外,存在两个计数器:强引用计数和弱引用计数。只要存在至少一个弱引用,控制块就不会被删除,即使对象已被销毁。
#include <memory>#include <iostream>
class Resource {public: Resource() { std::cout << "Resource acquired"; } ~Resource() { std::cout << "Resource destroyed"; } void doWork() { std::cout << "Working..."; }};
int main() { std::unique_ptr<Resource> uptr = std::make_unique<Resource>(); uptr->doWork(); // std::unique_ptr<Resource> uptr2 = uptr; // 编译错误! std::unique_ptr<Resource> uptr2 = std::move(uptr); // 可以
std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>(); std::shared_ptr<Resource> sptr2 = sptr1; // 引用计数 = 2 sptr1.reset(); // 引用计数 = 1 sptr2.reset(); // 引用计数 = 0,对象被销毁 return 0;}2. 虚函数表(vtable)与动态绑定机制
问题:"虚函数在内存层面是如何工作的?vtable存储在哪里?"
这个问题不仅考察对多态的理解,还考察对C++内存布局的认识。
- 对于每个至少有一个虚函数的类,编译器会创建一个静态表(vtable)。它包含指向虚函数的指针。
- 该类的每个对象都包含一个隐藏指针
__vptr,指向其所属类的vtable。 - 当通过指针或引用调用虚函数时,编译器生成的代码会通过
__vptr进入vtable,根据偏移量找到对应的函数并调用它。 - vtable存储在静态内存中(如同全局变量),而不是栈或堆中。
高级细节:多重继承带来的复杂性。在这种情况下,一个对象可能有多个__vptr,并且需要thunk函数(用于调整this指针)。这也是为什么C++中的多重继承被认为危险且效率较低的原因之一。
#include <iostream>
class Base {public: virtual void foo() { std::cout << "Base::foo"; } virtual ~Base() = def