跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 核心面试题与底层原理详解

综述由AI生成C++ 面试涵盖基础语法、内存管理、STL、并发及设计模式等核心领域。本文梳理了值传递、虚函数表、智能指针、RAII 原则及异常安全等关键知识点,并提供了 LRU 缓存、快速排序等经典算法模板。重点解析了 C++11 新特性、多线程同步机制及性能优化实践,帮助开发者系统掌握底层原理与工程落地细节。

岁月神偷发布于 2026/2/18更新于 2026/6/721 浏览

基础知识

C++ 基础语法

  1. C++ 和 C 的区别?
    • C++ 支持面向对象(封装、继承、多态)。
    • 引入了模板、STL 标准库以及异常处理机制。
  2. 值传递、指针传递、引用传递的区别?
    • 值传递:拷贝一份副本,开销较大。
    • 指针传递:传递地址,可修改原数据。
    • 引用传递:别名,语法更简洁且安全。
  3. const 的用法?
    • 修饰变量:声明常量。
    • 修饰指针:const int* p(指向常量),int* const p(常指针)。
    • 修饰成员函数:void f() const; 表示函数内不能修改成员变量。
  4. static 的作用?
    • 局部静态变量:函数调用间保持值。
    • 修饰全局变量/函数:只在文件内可见(内部链接)。
    • 修饰类成员:属于类而非对象,所有实例共享。
  5. inline 内联函数的原理?
    • 编译器用函数体替换调用点,减少函数调用开销。
    • 适用于小函数,频繁调用的场景。

面向对象

  1. C++ 四大特性?
    • 封装、继承、多态、抽象。
  2. 多态的实现方式?
    • 静态多态:函数重载、模板特化。
    • 动态多态:虚函数(通过虚函数表实现)。
  3. 虚函数、纯虚函数、抽象类区别?
    • 虚函数:子类可选择重写。
    • 纯虚函数:=0,子类必须实现。
    • 抽象类:含有纯虚函数,不能直接实例化。
  4. 虚函数表 (vtable) 的工作原理?
    • 类中有虚函数时,编译器生成 vtable,存储函数指针。
    • 对象包含 vptr,指向 vtable,实现动态绑定。
  5. 构造函数和析构函数的调用顺序?
    • 构造:先基类,再成员对象,最后派生类。
    • 析构:先派生类,再成员对象,最后基类。

内存管理

  1. C++ 内存分区?
    • 栈:局部变量、函数参数。
    • 堆:new/delete 分配的内存。
    • 全局/静态区:全局变量、静态变量。
    • 常量区:字符串常量。
    • 代码区:存放可执行代码。
  2. new/delete 与 malloc/free 的区别?
    • new 调用构造函数,返回指定类型指针。
    • malloc 只分配内存,不调用构造函数。
    • delete 调用析构函数,释放内存。
    • free 只释放内存。
  3. 内存泄漏如何检测?
    • 工具:Valgrind、ASan。
  • 手动:使用智能指针(shared_ptr, unique_ptr)自动管理生命周期。

  • C++11/14/17/20 新特性

    1. C++11 特性概览
      • auto 类型推导、nullptr、lambda 表达式、智能指针、右值引用、move 语义。
    2. 右值引用 & move 语义?
      • T&& 表示右值引用,用于接收临时对象。
      • std::move 转换为右值,避免拷贝,提高性能。
    3. 智能指针的区别?
      • unique_ptr:独占所有权。
      • shared_ptr:引用计数共享所有权。
      • weak_ptr:弱引用,不增加计数,解决循环引用。

    STL

    1. vector 和 list 的区别?
      • vector:连续存储,随机访问快,插入删除慢。
      • list:链表存储,插入删除快,随机访问慢。
    2. map 和 unordered_map 的区别?
      • map:红黑树实现,元素有序,O(log n)。
      • unordered_map:哈希表实现,无序,O(1) 平均。
    3. 迭代器失效问题?
      • vector 插入/删除时可能导致迭代器失效。
      • list 插入/删除不会影响其他迭代器。

    多线程与并发

    1. 线程创建方式?
      • std::thread
      • std::async
      • std::packaged_task
    2. 互斥锁和自旋锁区别?
      • 互斥锁:阻塞等待,适合长任务。
      • 自旋锁:忙等待,适合短任务。
    3. 条件变量 (condition_variable) 用法?
      • 结合 unique_lock,用于线程同步。

    设计模式

    1. 工厂模式、观察者模式 —— 常考理论。

    单例模式实现?

    class Singleton {
    private:
        Singleton() {}
    public:
        static Singleton& getInstance() {
            static Singleton instance; // C++11 保证线程安全
            return instance;
        }
    };
    

    常见算法题型

    1. LRU 缓存(哈希表 + 双链表)
      • 高频考点,需熟练掌握。

    快速排序模板

    void quickSort(vector<int>& a, int l, int r) {
        if (l >= r) return;
        int i = l, j = r, pivot = a[l];
        while (i < j) {
            while (i < j && a[j] >= pivot) j--;
            while (i < j && a[i] <= pivot) i++;
            if (i < j) swap(a[i], a[j]);
        }
        swap(a[l], a[i]);
        quickSort(a, l, i - 1);
        quickSort(a, i + 1, r);
    }
    

    二分查找模板

    int binarySearch(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) return mid;
            else if (nums[mid] < target) l = mid + 1;
            else r = mid - 1;
        }
        return -1;
    }
    

    综合类问题

    1. C++ 内存对齐规则?
    2. 深拷贝 vs 浅拷贝区别?
    3. 智能指针的循环引用问题怎么解决?
    4. 多态中析构函数为什么要设为虚函数?

    高级知识点

    对象生存期与资源管理(RAII / Rule of Five)

    • RAII:资源由对象构造获取(constructor),析构释放(destructor)。推荐把资源封装在对象里,避免裸 new/delete。
    • Rule of Five:如果定义了自定义析构、拷贝/赋值/移动中的任意一个,通常要考虑五个函数:~T()、T(const T&)、T& operator=(const T&)、T(T&&) noexcept、T& operator=(T&&) noexcept。
    • noexcept:移动构造/移动赋值应尽量标注 noexcept,因为很多 STL 容器在需要判断是否可用 noexcept move 时会选择拷贝或移动;若移动不是 noexcept,容器在扩容等操作时可能退回到拷贝(性能或语义影响)。

    示例(拷贝 - 移动 - 释放的正确实现):

    class Buffer {
        size_t n_;
        int* data_;
    public:
        Buffer(size_t n = 0) : n_(n), data_(n ? new int[n]() : nullptr) {}
        ~Buffer() { delete[] data_; }
    
        // copy
        Buffer(const Buffer& o) : n_(o.n_), data_(o.n_ ? new int[o.n_] : nullptr) {
            std::copy(o.data_, o.data_ + n_, data_);
        }
        Buffer& operator=(Buffer o) { // copy-and-swap 提供强异常安全
            swap(*this, o);
            return *this;
        }
    
        // move
        Buffer(Buffer&& o) noexcept : n_(o.n_), data_(o.data_) {
            o.n_ = 0; o.data_ = nullptr;
        }
        Buffer& operator=(Buffer&& o) noexcept {
            if (this != &o) {
                delete[] data_;
                n_ = o.n_; data_ = o.data_;
                o.n_ = 0; o.data_ = nullptr;
            }
            return *this;
        }
    
        friend void swap(Buffer& a, Buffer& b) noexcept {
            using std::swap;
            swap(a.n_, b.n_);
            swap(a.data_, b.data_);
        }
    };
    

    面试点:为什么 operator=(Buffer o)(按值)提供强异常安全?因为拷贝发生在进入函数时,随后 swap 保证不会抛异常;若拷贝失败,原对象不受影响。


    拷贝 vs 移动 vs 完美转发

    • std::move:将左值转换为右值(允许'移动'语义)。它只是类型转换,不做实际移动。
    • std::forward<T>:用于完美转发(保持值类别),常出现在模板转发场景(T&& 是 forwarding reference)。
    • 完美转发示例(容器 emplace 风格):
    template<typename T>
    void push_back_emplace(std::vector<T>& v, T&& val) {
        v.emplace_back(std::forward<T>(val)); // 保持传入值类别
    }
    

    面试点:区分 forwarding reference(模板 T&&)和纯右值引用。


    异常安全分级(面试必问)

    • 无保证(No guarantee):函数失败后程序状态不确定。
    • 基本保证(Basic):不泄露资源,对象处于有效但未定义的状态。
    • 强保证(Strong):要么成功,要么回滚到原状态(事务式)。
    • 不抛异常保证(No-throw):函数保证不抛异常(对析构函数很重要)。

    实现强保证常用技术:copy-and-swap、先构造新对象再替换。


    Undefined Behavior(UB)——必须会举例并解释

    常见 UB:

    • 访问释放后的内存(use-after-free)。
    • 双重释放(double free)。
    • 有符号整数溢出(int 溢出是 UB)。
    • 解引用空指针。
    • 同时无同步的并发读写(data race)。

    示例:

    int a = INT_MAX;
    int b = a + 1; // 有符号溢出 —— UB(不要假设会 wrap-around)
    

    面试点:说明 UB 会让编译器基于假设做优化,从而产生难以预期的行为。


    STL 深入(常被问到的细节)

    • push_back vs emplace_back:emplace_back 直接在容器末构造对象(避免一次临时拷贝/移动)。
    • reserve:对 vector 预分配容量以避免多次 realloc(均摊复杂度)。
    • 容器复杂度与迭代器失效规则(面试常问)。举例:
      • vector:reallocation(如 push_back 导致容量增长)会使所有指针/引用/迭代器失效;在中间 insert/erase 会使其后的迭代器失效。
      • list / forward_list:插入/删除不影响除被删除元素外的迭代器(稳定迭代器)。
      • map(平衡树):插入/删除不会使其他元素的引用/迭代器失效(除了被删除的)。
      • unordered_map:rehash 会使迭代器失效;插入可能导致 rehash。
    • allocator 基本概念:定制内存分配策略(进阶题)。

    面试点:能解释为什么对 vector resize 可能触发移动还是拷贝(取决于元素是否可 noexcept move)。


    并发与内存模型(非常重要)

    • 数据竞争(Data race):两个或多个线程无同步地访问同一内存位置,且至少一个为写,程序行为未定义。
    • std::mutex / std::lock_guard / std::unique_lock:RAII 锁封装;std::scoped_lock 用于多锁防死锁。
    • std::atomic<T>:提供原子操作和内存序(memory_order_relaxed/acquire/release/seq_cst)。
    • compare_exchange_weak vs compare_exchange_strong:weak 可能虚假失败(适用于循环),strong 不会。
    • ABA 问题:CAS 仅比较值,若中间值先改为 B 再改回 A 会误判。常用解决:加版本号(tagged pointer)、使用 hazard pointers 或垃圾回收策略。
    • 线程同步经典题:std::condition_variable 的使用(生产者 - 消费者),std::call_once 和 std::once_flag 做线程安全单例。

    示例(线程安全单例,C++11 更简单):

    MySingleton& instance() {
        static MySingleton inst; // C++11 保证线程安全的局部静态初始化
        return inst;
    }
    

    示例(简单生产者 - 消费者):

    std::mutex mu;
    std::condition_variable cv;
    std::queue<int> q;
    
    void producer() {
        {
            std::lock_guard lk(mu);
            q.push(42);
        }
        cv.notify_one();
    }
    
    void consumer() {
        std::unique_lock lk(mu);
        cv.wait(lk, []{ return !q.empty(); });
        int v = q.front();
        q.pop();
    }
    

    性能与优化实践(面试考点)

    • CPU 缓存友好(contiguous memory 优于链表),尽量让热点数据放在一起。
    • 减少内存分配(使用内存池 / reserve)。
    • 避免不必要的拷贝(move semantics、emplace)。
    • 关注分支预测、内联(inline)与编译器优化,先用 profiling(perf / gprof)确认热点,再优化。
    • 提前测量:microbenchmark(防止过早优化)。

    常见进阶题与样例实现(面试常问,附模板)

    a) LRU Cache(O(1) get/put)
    class LRUCache {
        int cap;
        list<int> keys;
        unordered_map<int, pair<int, list<int>::iterator>> mp;
    public:
        LRUCache(int capacity) : cap(capacity) {}
    
        int get(int k) {
            auto it = mp.find(k);
            if (it == mp.end()) return -1;
            keys.splice(keys.begin(), keys, it->second.second);
            return it->second.first;
        }
    
        void put(int k, int v) {
            auto it = mp.find(k);
            if (it != mp.end()) {
                it->second.first = v;
                keys.splice(keys.begin(), keys, it->second.second);
                return;
            }
            if ((int)mp.size() == cap) {
                int old = keys.back();
                keys.pop_back();
                mp.erase(old);
            }
            keys.push_front(k);
            mp[k] = {v, keys.begin()};
        }
    };
    

    面试点:解释 splice 的常数复杂度和为什么使用 list + unordered_map。

    b) 线程安全单例(call_once)
    class S {
    public:
        static S& instance() {
            static std::once_flag f;
            static S* p = nullptr;
            std::call_once(f, []{ p = new S(); });
            return *p;
        }
    private:
        S() = default;
    };
    
    c) Copy-swap 异常安全赋值

    (见 Buffer 示例)


    调试与检测工具(面试可能问会用哪些)

    • AddressSanitizer (ASan):检测内存越界、use-after-free。
    • UndefinedBehaviorSanitizer (UBSan):检测 UB(如有符号溢出)。
    • ThreadSanitizer (TSan):检测 data race。
    • Valgrind:检测内存泄漏(Linux)。
    • gdb / lldb:调试断点、查看 backtrace。
    • perf / Flamegraphs:性能分析。

    高频面试问题(附要点回答)

    • 为什么要用 unique_ptr 而不是裸指针?
      → 表达所有权,自动释放,防止泄漏;shared_ptr 代价 (引用计数) 比 unique_ptr 高,且会引入循环引用风险。
    • std::move 之后对象状态如何?
      → 留在'可析构但未指定状态',只能赋值或析构;使用前须重新赋值或立即处理。
    • volatile 在 C++ 中的作用?
      → 不用于线程同步;仅抑制某些编译器优化,真正并发应使用 std::atomic。
    • 如何避免死锁?
      → 统一锁顺序、使用 std::scoped_lock、用 try_lock 超时退让、减少锁粒度。
    • 如何写高性能 IO / 内存敏感代码?
      → 减少 system call、使用缓冲、减少分配、考虑内存对齐/预取/向量化。

    小建议

    • 练习方式:读题后写出 O(1)/O(n) 解法,然后讨论边界、异常安全、并发假设与性能瓶颈。
    • 面试时不要只写能过的代码,还要能解释时间/空间复杂度、是否有 UB、异常安全级别、并发安全假设与潜在改进。

    目录

    1. 基础知识
    2. C++ 基础语法
    3. 面向对象
    4. 内存管理
    5. C++11/14/17/20 新特性
    6. STL
    7. 多线程与并发
    8. 设计模式
    9. 常见算法题型
    10. 综合类问题
    11. 高级知识点
    12. 对象生存期与资源管理(RAII / Rule of Five)
    13. 拷贝 vs 移动 vs 完美转发
    14. 异常安全分级(面试必问)
    15. Undefined Behavior(UB)——必须会举例并解释
    16. STL 深入(常被问到的细节)
    17. 并发与内存模型(非常重要)
    18. 性能与优化实践(面试考点)
    19. 常见进阶题与样例实现(面试常问,附模板)
    20. a) LRU Cache(O(1) get/put)
    21. b) 线程安全单例(call_once)
    22. c) Copy-swap 异常安全赋值
    23. 调试与检测工具(面试可能问会用哪些)
    24. 高频面试问题(附要点回答)
    25. 小建议
    • 💰 8折买阿里云服务器限时8折了解详情
    • Magick API 一键接入全球大模型注册送1000万token查看
    • 🤖 一键搭建Deepseek满血版了解详情
    • 一键打造专属AI 智能体了解详情
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • Flutter 跨平台开发入门与实战指南
    • 扣子(Coze)Skills+OpenClaw 实战:零基础打造 AI 智能体
    • CentOS 7/8/9 安装 MySQL 8.0+ 完整指南
    • Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码
    • Jan:开源本地大模型客户端,支持离线部署与 API 服务
    • uniapp+python微信小程序毕业生招聘平台
    • PyCharm 中 GitHub Copilot 插件安装与配置指南
    • OpenClaw 在 Windows 环境下的 WSL 部署与飞书配置指南
    • Flutter 组件 genkit 适配鸿蒙:AI 幻觉审计与端云协同 RAG 方案
    • Llama-Factory 部署常见错误与解决方案
    • Llama-2-7b 昇腾 NPU 测评:核心性能数据、场景适配与硬件选型
    • C++ String 类实战:常见算法题解析与优化
    • WinBtrfs 在 Windows 下读写 Linux Btrfs 分区指南
    • 高鋒集團合夥人兼 Web3Labs 行政總裁黃俊瑯:以資本與生態賦能傳統企業 Web3 轉型
    • C++ STL list 容器详解:使用与模拟实现
    • 华为手机鸿蒙系统安装 Google Play 方案对比及操作教程
    • Vivado 2023.2 安装教程:从零搭建 FPGA 开发环境
    • DeepSeek V4 发布:核心能力与落地场景解析
    • Git Hooks 详解与实战应用
    • LLaMA3:开源战胜闭源意味着什么

    相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

    • HTML转Markdown

      将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online