【八股速成】C++篇:面试一本通!全覆盖基础到高级知识点,大厂速成宝典!

Hello 牛友们!👋

马上又是一年一度的 春招/秋招/社招高峰期,相信有不少 C++ 开发的同学已经开始准备大厂面试了。但正如我们都知道的,C++ 是最“硬核”的语言之一,想要拿下心仪 offer,准备扎实的 C++ 八股文是通关面试的必备武器

这篇文章,我特别整理了 C++ 经典面试中几乎 100% 涉及的知识点,从基础到高级,覆盖 C语言差异、STL容器、内存管理、C++11进阶、多线程、虚函数、多态底层等多方面模块。内容足够详细,直接看完就可以背,全面提升你的面试通过率!

🌟 目录

  1. C++ 基础篇
  2. 内存管理篇
  3. STL 容器篇
  4. 面向对象设计篇
  5. C++11/14/17 新特性篇
  6. 多线程与并发篇
  7. 高级优化与实际场景篇

🌟 1. C++ 基础篇

1️⃣ const 与 constexpr 的区别

  • const: 用于定义变量为只读,在运行时只初始化一次。常见用法:修饰局部变量、常量对象方法、函数参数(避免拷贝)。
  • constexpr: C++11 引入,要求变量或表达式在 编译期间计算,如代码中的常量折叠。常见用法:声明常量值、constexpr 函数 提高运行效率。
  • 面试考点:面试官很喜欢纠结 const 和 constexpr 的区别(编译时 vs 运行时常量)。例如:constexpr 可用于模板参数,而 const 不行。

2️⃣ inline 与宏的区别

  • #define 宏:宏在预处理阶段展开,没有类型检查,容易导致调试错误等问题。
  • inline 函数:编译器尝试将函数展开,语法和调试更容易。
  • 面试常问:为什么 C++ 推荐使用 inline 替代宏?答案:因为 inline 保留了类型检查和作用域规则,宏缺失这些特性。

3️⃣ explicit 的作用

  • 问题场景:如果单参数构造函数没有 explicit 修饰,编译器可能会进行隐式转换导致不期望的结果。如:class A { ... }; A a = 10; (隐式调用构造函数)。
  • 解决:加 explicit!防止隐式转换,避免类型错误。

4️⃣ 函数重载与函数重写的区别

  • 函数重载(Overloading):函数同名,但参数列表不同(类型、个数、顺序)。发生在 同一作用域内。
  • 函数重写(Overriding):子类重新定义父类的虚函数。子类必须与父类函数保持原始签名一致。

5️⃣ 左值和右值

  • 左值有名字、可以取地址的对象。
  • 右值临时对象,无法直接取地址,如字面量或表达式的结果。
  • 面试例子:右值引用 &&std::move 的使用场景?答案:常用于转移资源所有权,提升性能。

6️⃣ 四种类型转换

  • 强制类型转换会引发危险行为,而 C++ 提供了更安全的类型转换方式: static_cast:编译时类型检查,主要用于普通值类型转换。dynamic_cast:用于类层次的上行或安全的下行转换。const_cast:在类型中加入或移除 const。reinterpret_cast:直接重新解释二进制,是最危险的转换。

🌟 2. 内存管理篇

1️⃣ 堆和栈的内存区别

  • 栈(Stack):自动分配,回收速度快,用于局部变量和函数调用。
  • 堆(Heap):动态分配,生命周期由 new/delete 或智能指针控制。

2️⃣ 内存泄漏与 RAII

  • 内存泄漏: 没有释放已分配的堆内存,导致内存增长和程序崩溃。
  • RAII(资源管理即初始化):用类的构造函数管理资源分配,析构时释放资源,避免手动管理内存。

3️⃣ 智能指针

C++11 引入了智能指针,三种都很重要:

  • std::unique_ptr:独占所有权。
  • std::shared_ptr:共享所有权,计数器管理引用。
  • std::weak_ptr:解决 shared_ptr 循环引用问题,不持有资源。

面试常问:shared_ptr 和 unique_ptr 的底层实现区别?

答:

  • unique_ptr:所有权转移实现了通过 std::move,无引用计数。
  • shared_ptr:内部通过引用计数器(控制块)管理资源。如果最后一个 shared_ptr 被释放,资源析构。

4️⃣ 内存对齐

  • 为了提升访问速度,C++ 会对结构体进行字节对齐。
  • 对齐规则:对于 n 字节对齐,变量的偏移量为 n 的整数倍。
  • 面试例子:资源利用率 vs 性能如何权衡?(内存对齐 vs 空间)

🌟 3. STL容器篇

STL 是 C++ 面试的必考点,占比非常大。

1️⃣ 容器分类

  • 顺序容器:vector, deque, list
  • 关联容器:map, set(基于红黑树)。
  • 无序容器:unordered_map(基于哈希表)。

2️⃣ 常见容器底层实现

  • vector:底层为动态数组,支持快速随机访问,扩容为原大小的 2 倍。
  • std::map:基于红黑树,查找/插入/删除时间复杂度 O(logN)。
  • unordered_map:基于哈希表,查找是平均 O(1)。

3️⃣ std::string

面试必问:string 和 char 的区别?*

答:

  • std::string 是容器类,支持 STL 的方法,C++ 底层自动管理内存。
  • char* 是 C 风格字符串,需要手动分配和释放内存。

🌟 4. 面向对象特性篇

1️⃣ 多态

  • 实现动态继承:需父类声明为虚函数,通过 vtable(虚表)实现动态分派。
  • 纯虚函数:=0,实现抽象类。
  • 常考陷阱:析构函数为非虚函数,可能导致内存泄漏。

2️⃣ 三大特性:封装、继承、多态

  • 封装:通过访问权限 (private/protected/public) 控制外部访问。
  • 继承:指父类和子类的关系,支持代码复用。
  • 多态:同一函数实现动态方法绑定。

🌟 5. C++ 11/14/17 特性篇

核心特性

  • 右值引用、std::move:提升性能,避免不必要的拷贝。
  • lambda 表达式:简化函数对象,快速传递匿名函数。
  • constexpr:编译时计算常量,提升性能。

🌟 6. 多线程与并发篇

1️⃣ std::thread

  • 使用 std::thread 创建线程,结合 std::mutex 实现同步。
  • 面试必会:thread 的生命周期管理,以及 joindetach 的区别。

2️⃣ 锁机制

  1. std::mutex:用于线程间锁定同步。
  2. RAII式锁:std::lock_guard,释放控制权更安全。
  3. std::shared_mutex:实现多读单写,提高性能。

3️⃣ Atomic 操作

  • 常用的 std::atomic,实现无锁编程,避免数据竞态。

🔗 C++校招真实学习路径(附免费资源)

1. C++基础强化

2. 八股文系统梳理

3. 算法代码模板

📚 校招重点模块免费资料

1. 虚函数体系

  • 虚函数表可视化工具https://godbolt.org/ (输入C++代码查看汇编层面的虚函数调用)

2. 智能指针

3. 多线程编程

🚀 实战建议(三天速成计划)

  1. 第一天:八股速记早晨:虚函数+智能指针(各刷20道牛客题)下午:STL容器对比(手写vector/map核心API)晚上:多线程锁机制(重点理解条件变量)
  2. 第二天:代码实操手撕智能指针(实现引用计数版shared_ptr)白板编码:反转链表/二叉树遍历(严格计时15分钟)调试:Valgrind检测内存泄漏(重点解决循环引用问题)
  3. 第三天:模拟面试使用牛客模拟面试功能:https://www.nowcoder.com/interview/ai/index高频考点自测表: ✅ 虚析构函数的作用(5秒内能答出) ✅ shared_ptr线程安全级别(能画控制块结构图) ✅ vector迭代器失效场景(能举3个例子)

总结:

这篇文章总结了从基础到高级的 C++ 面试知识点,以及关键考题与答题思路。建议大家收藏+频繁回顾,本篇背熟了应对大厂面试轻松拿捏!🔥

觉得有用记得点赞➕收藏,让更多人看到!!

🔗 额外福利:

如果你对拼多多感兴趣,我这里有内推机会哦~可以直接私信我或者 https://careers.pddglobalhr.com/campus/grad?t=Ni2uKcwcYX (春招版),https://careers.pddglobalhr.com/campus/intern?t=JUlctG4zA8 (实习版)#八股##c++#Hello 牛友们!👋

#牛友职场人脉来了#
全部评论

相关推荐

03-22 11:53
已编辑
门头沟学院 C++
1. 被free回收的内存是立即返回给操作系统吗?2. sizeof和strlen的区别?3. new/delete、malloc/free的区别4. 堆快一点还是栈快一点?5. 在main执行之前和执行之后执行的代码可能是什么?6. final和override的关键字?7. 拷贝初始化和直接初始化?8. select、poll、epoll9.Reactor?10. 阻塞、非阻塞、同步、异步?11. Proactor?1. 被free回收的内存首先会被ptmalloc使用双链表保存起来,当有用户申请内存的时候,会直接从这里面找出合适的内存进行返回。这样可以避免操作系统的频繁调用,另外它还会将小内存进行合并,防止产生过多的内存碎片;2. sizeof是运算符,strlen是库函数。sizeof的参数可以是任何数据的类型或数据,strlen的参数只能是字符串的指针,且结束符为'\0'的字符;3. 前者是C++运算符,后者是C/C++库函数;new自动计算要分配的内存对象,malloc需要计算;new是类型安全的,malloc不是;调用new的给自定义对象分配内存的时候,会调用构造函数并分配相应的内存,delete的时候会释放内存并执行析构;4.  栈快一点,操作系统堆栈提供支持,有分配专门的寄存器存放栈的地址,栈的出入栈也十分简单,并且有专门的指令执行,所以栈的效率更高更快;而堆在分配的内存的时候要使用算法寻找合适大小的内存,并且在获取堆的内容的时候需要两次访问,一次指针,一直是指针保存的地址5. 初始化全局变量和静态变量,即.data段数据,将未初始化的全局变量进行赋值;全局对象初始化;将argc、argv传递给main函数;___attribute___(constructor);结束后,全局的析构函数会在main函数执行完后析构,可用一个atexit注册一个函数会在main函数之后执行;___attribute___(destructor)6. override的关键字,指定了子类的这个虚函数必须重写父类的,如果函数名不小心打错了,编译器会进行报错,不会通过;当不希望某个类被继承、或不希望虚函数被重写可以在类名和虚函数后面添加final关键字;7. 直接初始化直接调用与实参匹配的构造函数;拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个零食对象拷贝到正在创建的对象;8. select函数将已连接的socket都放到一个文件描述符集合,然后调用select函数将文件描述符拷贝到内核中,去遍历进行检测那个套接字发生事件,将其进行标记,后续再复制到用户态,进行遍历找到标记的套接字。这其中发生了2次遍历文件描述符集合和2次拷贝文件描述符集合。poll突破文件描述符个数的限制(1024),通过一个动态数组,以链表的形式管理。其它与select还是一样的。epoll通过在内核中使用红黑树来跟踪进程所有待检测的文件描述符,把需要监控的socket通过epoll_ctl函数加入到内核中的红黑树,第二,epoll使用时间驱动的机制,内核里维护了一个链表来记录就绪事件,当某个socket事件发生时,通过回调函数内核将其加入到就绪事件列表中,当用户调用epoll_wait函数时,只会返回有事件发生的文件描述符的个数。9. reactor模型是 I/O 多路复用监听事件,收到事件后,根据事件类型分配(Dispatch)给某个进程 / 线程。单reactor单进程:reactor对象通过epoll监听事件,收到事件后通过dispatch进行分发,如果是连接建立的事件交个Acceptor对象进行处理,如果不是就交给Handle对象通过read->业务处理->send的流程来完成完整的业务流程。单reactor多线程:对于不是连接建立事件,则交由Handler对象处理,他不负责业务处理,只负责数据的接受和发送,数据会发送给线程池来进行业务处理,处理完成后将业务结果返回给handler对象,然后发送给client;多reactor多线程:主线程只负责接受新连接,子线程负责完成后续的业务处理。主线程只需要把新连接传给子线程,子线程进行业务处理后,直接可以将处理结果发送给客户端。10. 阻塞I/O:当用户执行read,线程会被阻塞,一直等到内核数据准备好[1],并把数据从内核缓冲区拷贝到应用缓冲区中[2],当拷贝完成,read才返回。非阻塞I/O:可以在数据未准备好久立即返回,然后应用程序不断轮训内核,直到数据准备好,[2]操作还是要的;异步I/O:上面的[1]和[2]的步骤都不需要等待,都交由内核完成。11. Reactor模式是基于待完成的I/O事件,而Proactor模式是基于[已完成]的I/O事件。
点赞 评论 收藏
分享
评论
2
4
分享

创作者周榜

更多
牛客网
牛客企业服务