面经

C++ 基础

  • static 的作用是什么?
    • 修饰局部变量: 只初始化一次局部变量,生命周期延长到整个程序运行期间,但作用域仍然只在当前函数内。
    • 修饰全局变量/函数: 表示该符号只在当前源文件内可见,避免和其他文件同名冲突。
    • 修饰类的成员变量: 表示这个成员变量属于整个类,而不是某个对象。所有对象共享同一份静态成员变量。
    • 修饰类的成员函数: 表示这个函数属于类本身,调用时不依赖具体对象。
  • 修饰类成员函数的作用是什么?什么情况下要用静态成员函数?举一个场景
    • 作用: 静态成员函数属于类,而不属于某个对象,因此:
      • 可以不创建对象直接调用;
      • 不能访问非静态成员变量/非静态成员函数;
      • 可以访问静态成员变量/静态成员函数;
    • 使用时机: 当一个函数的逻辑不依赖某个具体对象的状态,但又希望它在 “类的命名空间” 下时,就适合定义成静态成员函数。
    • 场景: 统计对象个数;
  • const 静态成员函数内部能否修改类的非静态变量?为什么?
    • C++ 里静态成员函数不能声明为 const
      • 因为 const 成员函数本质上修饰的是隐藏参数 this
      • 但静态成员函数没有 this 指针;
    • 静态成员函数内部不能直接修改类的非静态变量
      • 非静态成员变量属于对象;
      • 静态成员函数没有 this
      • 所以它不知道要改哪个对象的成员;
    • 如果拿到了对象,就可以修改
  • 为什么要进行内存对齐?
    • 内存对齐是为了让数据按特定边界存放,比如 4 字节数据放在 4 的倍数地址,8 字节数据放在 8 的倍数地址;
    • 主要用来 提高访问效率 以及 硬件平台要求必须对齐
  • 什么是虚函数?它的作用是什么?
    • 定义: 在基类中用 virtual 修饰的成员函数就是虚函数。
    • 作用: 虚函数用于实现运行时多态。当我们通过基类指针或基类引用调用函数时,会根据对象的真实类型决定调用哪个函数。
  • 虚函数的底层实现机制是什么?
    • 核心: 虚函数表(vtable)+ 虚函数表指针(vptr)。
    • 机制:
      • 有虚函数的类,编译器通常会为它生成一张虚函数表;
      • 对象中会隐藏一个指针 vptr,指向所属类的虚函数表;
      • 表中存放虚函数地址;
      • 调用虚函数时,通过 vptr -> vtable 查找函数地址再调用;
  • 动态绑定和静态绑定的区别是什么?
    • 静态绑定: 也叫 早绑定编译期确定 调用哪个函数;
    • 动态绑定: 也叫晚绑定,运行期确定 对象的真实类型决定调用哪个函数;

STL 容器与数据结构

  • std::map 是怎么实现的?特性和复杂度是多少?
    • std::map 底层一般是红黑树实现;
    • 红黑树是一种自平衡二叉搜索树,保证树高近似 O(log n)
    • key 自动有序;
    • 查找、插入、删除复杂度都稳定在 O(log n)
  • std::mapstd::unordered_map 的区别是什么?
    • std::map
      • 底层:红黑树;
      • 元素有序;
      • 查找/插入/删除:O(log n)
      • 支持范围查询;
    • std::unordered_map
      • 底层:哈希表;
      • 元素无序;
      • 平均查找/插入/删除:O(1)
      • 最坏情况可能退化为 O(n)
  • 为什么设计这两个不同的 Map?各自的使用场景是什么?
    • map 解决 “有序 + 稳定 logN 的需求;
    • unordered_map 解决 “无序 + 平均 O(1) 的需求。
  • vector 访问元素时,[].at() 有什么区别?
    • operator[]
      • 不做边界检查
      • 访问快
      • 越界是未定义行为
    • .at()
      • 会做边界检查
      • 越界会抛出 std::out_of_range
      • 更安全,但略慢
  • 如何释放 vector 占用的多余内存?
    • vector 的容量和大小是分开的。删除元素后,容量通常不会立刻缩小。
    • std::vector<int>(v).swap(v);
  • reserveresizeclear 的区别是什么?
  • 场景题:如果需要频繁读取,选什么数据结构?如果需要频繁写入,选什么?
    • 频繁读取:如果是顺序遍历、随机访问很多,优先选 vector / 数组;
    • 频繁写入
      • 尾插很多: 还是 vectorO(1) 的时间复杂度,并且可以提前 reserve
      • 中间频繁插入: 可考虑 **链表 list**,已知位置插删 O(1)

计算机网络与操作系统

  • 为什么 HTTPSHTTP 更安全?
    • 因为 HTTP 是明文传输,而 HTTPS = HTTP + TLS/SSL
    • HTTPS 提供了三件关键能力:
      • 加密性: 防止数据被窃听;
      • 完整性: 防止数据在传输过程中被篡改;
      • 身份认证: 通过数字证书验证服务器身份,防止伪装网站、中间人攻击。
  • HTTPS 的加密机制是什么?
    • 握手阶段用非对称密码体系解决身份认证和密钥协商
    • 数据传输阶段用对称加密保证高效通信。
  • 线程之间是怎么通信的?
    • 线程属于同一进程,天然共享进程地址空间,所以线程通信最常见就是共享内存。
    • 常见线程通信方式
      • 共享变量 + 锁
      • 条件变量 condition_variable
      • 信号量;
  • 什么是死锁?产生死锁的条件是什么?
    • 定义: 多个线程互相等待对方持有的资源,导致所有线程都无法继续执行,这就是死锁。
    • 条件:
      • 互斥: 资源一次只能被一个线程占用;
      • 请求并保持: 线程已经持有资源,同时又请求新资源;
      • 不可剥夺: 资源不能被强行抢走,只能主动释放;
      • 循环等待: 存在资源等待环;
  • 如何避免或解决死锁问题?
    • 固定加锁顺序;
    • 一次性申请所有资源;
    • 使用 try_lock