海康威视 C++开发 一面
1. 自我介绍
2. C++11 之后你常用的新特性有哪些
答案:比较常用的有 auto、nullptr、右值引用、移动语义、lambda、thread、mutex、condition_variable、atomic、shared_ptr、unique_ptr、constexpr、范围 for、unordered_map。如果放到工程里,最常见的收益主要有三类:一类是简化代码书写,比如 auto、lambda;一类是提升资源管理能力,比如智能指针和 RAII;还有一类是标准并发库,让多线程开发不再完全依赖 pthread。真正答这题时,不用把特性背得太全,挑几个自己确实用过的讲清楚更重要,比如移动语义解决了哪些不必要的拷贝,lambda 在回调或算法里怎么简化代码,atomic 用在什么场景。
代码:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4};
auto sum = 0;
for (auto x : nums) sum += x;
auto f = [](int a, int b) { return a + b; };
cout << sum << endl;
cout << f(3, 5) << endl;
return 0;
}
3. 智能指针有哪几种,分别适合什么场景
答案:常见的是 unique_ptr、shared_ptr、weak_ptr。unique_ptr 表示独占所有权,适合对象生命周期非常明确、不会被多个模块共同持有的场景,也是现代 C++ 里默认优先考虑的智能指针。shared_ptr 表示共享所有权,适合资源会被多个对象共同使用、释放时机不容易由单方决定的情况。weak_ptr 不拥有对象,只观察对象是否还存在,主要用于打破 shared_ptr 循环引用。工程里最容易出问题的不是不会用,而是所有权设计不清楚,把本来独占的对象也到处传 shared_ptr,最后生命周期越来越混乱。
代码:
#include <iostream>
#include <memory>
using namespace std;
struct A {
~A() { cout << "~A\n"; }
};
int main() {
unique_ptr<A> p1 = make_unique<A>();
auto p2 = make_shared<A>();
weak_ptr<A> wp = p2;
if (auto sp = wp.lock()) {
cout << "alive\n";
}
return 0;
}
4. 什么是 RAII,为什么它在 C++ 里很重要
答案:RAII 的核心思想是把资源的获取和释放绑定到对象的构造与析构上。资源不只是内存,也包括文件句柄、锁、socket、数据库连接、线程句柄等。它重要的地方在于,无论函数是正常结束、异常退出,还是中途 return,只要对象生命周期结束,析构就会自动触发,资源就能被可靠释放。很多内存泄漏、忘记解锁、句柄没关闭的问题,本质上都能通过 RAII 思路规避。
代码:
#include <mutex>
using namespace std;
mutex mtx;
void work() {
lock_guard<mutex> lock(mtx);
// 临界区逻辑
}
5. C++ 的四种强制类型转换分别是什么
答案:static_cast 主要用于编译期可确定的类型转换,比如数值类型转换、上行转换等。dynamic_cast 主要用于多态体系里的安全向下转型,底层依赖 RTTI。const_cast 用来增加或去掉 const / volatile 属性。reinterpret_cast 更偏底层,直接从比特层面重新解释类型,风险最大。如果面试里问到这个,一般不只是让你背名字,而是想看你是否知道该什么时候避免使用转换,尤其是 reinterpret_cast 和不安全的 const_cast。
6. 讲一下红黑树,为什么很多标准库容器会用它
答案:红黑树是一种自平衡二叉搜索树,通过颜色约束和旋转操作,保证树的高度不会退化得太夸张,因此查找、插入、删除都能维持在 O(logn)。它适合需要动态维护有序集合的场景,所以像 map、set 这类关联容器通常会用红黑树作为底层结构。相比普通二叉搜索树,它不会因为数据有序插入就退化成链表;相比 AVL,红黑树在插入删除时旋转调整通常更少,更适合频繁更新。所以标准库偏向它,不是因为它最完美,而是综合性能和工程实现比较平衡。
7. 红黑树插入后为什么要做调整,查找效率为什么是对数级
答案:插入新节点后可能会破坏红黑树原有的颜色规则,比如出现连续红节点,或者让某些路径的黑高不一致。所以要通过变色和左旋右旋把树重新调整回满足性质的状态。它查找效率能维持在 O(logn),关键在于红黑树保证了最长路径不会超过最短路径的两倍,整体高度始终受控。这也是为什么红黑树虽然不是绝对最矮的平衡树,但足够稳定。
8. vector 的扩容机制是什么,为什么会导致迭代器失效
答案:vector 是连续内存存储,容量不够时会重新申请一块更大的连续空间,把旧元素搬过去,再释放原来的内存。扩容倍数标准没有强制死规定,但常见实现会按 1.5 倍或 2 倍增长。一旦扩容,原来那块内存地址就变了,所以之前拿到的迭代器、引用、指针都可能指向失效地址。如果提前知道元素规模,可以先 reserve,这样可以明显减少扩容次数和元素搬移成本。
代码:
#include <vector>
using namespace std;
int main() {
vector<int> v;
v.reserve(1000);
for (int i = 0; i < 1000; ++i) {
v.push_back(i);
}
return 0;
}
9. select、poll、epoll 的区别是什么
答案:select 有文件描述符数量上限,而且每次调用都要把监听集合从用户态拷贝到内核态,返回后还要遍历所有 fd 看谁就绪。poll 去掉了固定上限,但仍然需要线性扫描。epoll 把“注册事件”和“等待事件”拆开,内核维护就绪队列,返回时直接给出活跃事件,所以在高并发连接场景下更高效。这题如果继续往下问,通常就会延伸到 LT、ET、惊群和 Reactor 模型。
10. GDB 调试版本通常在 Makefile 里怎么配
答案:最关键的是打开调试信息,也就是编译时加 -g。如果想让
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.
查看4道真题和解析