语言
1、static与const的作用
static修饰静态变量,const修饰常量(只读)。
static(局部):变量声明周期延长、不随函数结束销毁。只初始化一次
static(全局):限制作用域,只在当前文件可见
static(类内):所有对象共享同一份变量、函数
常量指针:指针指向的“值”不能改,但指针的“地址”可以改。const int *p,*靠近的内容不可改
指针常量:指针本身不能改(不能指向别的地方),但指向的内容可以改。int * const p = &a
2、引用与指针的作用以及区别?
引用必须初始化,指针可以不初始化。引用不能改变绑定对象,指针可以改变指向。引用没有 NULL,指针可以为 NULL。引用使用更安全,语法更简洁。引用本质是常量指针
3、如何避免野指针
野指针就是指向非法内存的地址,例如未初始化指针、指向已释放的内存、指向超出作用域的对象、指针越界。
避免方法:
- 指针声明后立刻初始化
- delete 后立即置为 nullptr。delete p; p = nullptr; // 避免悬空指针
- 数组操作必须确保不越界
- 使用智能指针代替裸指针
- 指针使用前必须检查是否为 nullptr
- 尽量使用引用而不是指针
- 避免多次释放同一块内存
- 避免返回栈上变量的地址
空指针:指向“无效地址”的指针,它不指向任何有效对象。int* p = nullptr;
4、malloc、free和new、delete区别?malloc申请空间失败怎么办?
malloc和free是标准库函数,而new和delete是C++操作符,可以被重载。
malloc失败后返回NULL,而new失败后抛出异常:std::bad_alloc
5、extern有什么作用
- 跨文件共享全局变量。
- C/C++混合编译
并可通过 extern "C" 关闭 C++ 名字修饰以实现与 C 语言链接的兼容。
extern 声明一个变量或函数在其他文件中定义,避免重复定义,同时允许跨文件访问。
它本身不分配内存,只告诉编译器这个符号在别处存在;
6、简述strcpy、sprintf与memcpy的区别?
- strcpy:拷贝字符串。“/0”结束。用于复制字符串、字符数组操作
- sprintf:格式化输出到字符串。用于拼接字符串,格式化输出,数字转为字符串
- memcpy:拷贝内存块。复制结构体、复制数组
7、C++强制类型转换使用场景
有四种强制转
- static_cast:基础类型之间的转换,最常用、最安全的编译期转换
- reinterpret_cast:任意类型转换不安全。
- const_cast:修改对象的“const 性质”。去掉/添加 const 属性
- dynamic_cast:安全,多态向下转换(父->子)
优先级:static_cast > dynamic_cast > const_cast > reinterpret_cast
8、什么时候使用默认构造函数?
9、什么时候生产默认拷贝构造函数
默认构造函数 = 无参数构造函数。需要先创建对象,而不是立即给构造参数时,就需要默认构造函数。
-
创建对象数组(最典型)
-
STL 容器默认初始化元素
-
类成员默认构造
-
new[] 动态数组
-
操作符
map[key] -
框架需要默认构造反射对象
-
想让类“可直接定义对象”时
10、什么是深拷贝什么是浅拷贝?默认构造函数是哪种拷贝?什么时候使用深拷贝,以及零拷贝
浅拷贝:拷贝指针,不拷贝指向的内容。共享同一块堆内存
深拷贝:开辟一个新内存,将内存放进去。防止数据污染
默认构造函数使用浅拷贝
零拷贝:在网络传输、文件 IO 中,避免在用户态/内核态之间重复复制数据,通过共享内存、DMA、映射技术直接传输数据。减少数据在内核与用户态之间的复制,提升网络传输与大型文件处理性能。
标准库
1、vector的reserve与resize的区别?
reserve:只改变容量(capacity),不改变元素数量,不创建元素。预分配容量,但不创建元素
resize:只改变元素数量(size),会创建或销毁元素。改变 size,创建或删除元素
2、list的底层实现原理
list是双向链表,插入删除O(1),随机访问O(n)。缺点是不能随机访问,遍历慢,空间开销较大。
3、deque的底层实现原理
双端队列。兼具vector与list的部分优点。不是连续内存,插入访问O(1)
std::deque 的底层是分段连续的动态数组:多个固定大小的 buffer 由 map(指针数组)管理。
支持 O(1) 的双端插入,是因为通过增加/减少 buffer 而不需要整体搬移数据。
支持 O(1) 随机访问,是因为 index 可以通过计算找到对应的 buffer 和 offset。
4、什么时候使用vector、list和deque?
大部分情况用vector、双端队列用deque、任意位置插入/删除是 O(1)用list。
5、priority_queue底层实现原理
优先级队列,是一个基于堆的容器适配器,用vector保存数据。维护完全二叉堆,插入删除O(log n),访问O(1)
6、map、set、multimap、multiset底层实现原理,红黑树原理?
这些底层都是红黑树。
红黑树是有序的,五大性质:
- 每个节点要么黑色要么红色
- 根是黑色
- 所有叶子(NIL)节点是黑色
- 红色节点的子节点必须是黑色
- 从任意节点到其所有叶子路径上,黑色节点数量相同
红黑树靠五大性质维持平衡,操作通过“旋转 + 变色”修复结构。
7、unordered_map、unordered_set底层,哈希表原理?
底层实现是哈希表+拉链法。插入、查找、删除平均 O(1),最坏 O(n)。无序
哈希表核心思想:通过哈希函数把 key 映射到数组索引,再在该槽位处理冲突。
冲突是不可避免的,方法有:链地址法、
8、迭代器底层实现原理?种类
迭代器 = 封装指针的对象 + 重载运算符
它以统一方式访问容器内部元素,而不暴露容器的底层结构。
迭代器本质是“指针或指针封装”,作为统一访问方式。
vector/deque:指针或类似指针 → 随机访问 iterator。
list:节点指针 → 双向迭代,不支持随机访问。
map/set:红黑树节点指针 → 双向迭代,中序遍历。
unordered_xxx:哈希桶链表 → 前向迭代。
标准迭代器共五种类型:输入、输出、前向、双向、随机访问。
9、迭代器失效?连续存储容器失效?非连续存储容器失效?处理方式
迭代器失效就是迭代器内部指向的元素被移动、删除或其地址变化,导致迭代器不再指向合法数据,使用就会触发未定义行为。
连续存储容器的迭代器失效:因为扩容、插入、删除会导致内存重分配或元素整体移动。
非连续存储容器失效:只有被删除元素的迭代器会失效
技巧:
- 使用erase和insert返回值
- 先记录再删除
- 使用稳定的容器操作
vector: 插入或删除元素后,该位置及其后面的迭代器都会失效;如果重新分配内存,所有迭代器都会失效。 list/forward_list: 只有被删除元素的迭代器会失效。 map/set/multimap/multiset: 只有被删除元素的迭代器会失效。 unordered_map/unordered_set: 插入操作可能导致所有迭代器失效(rehash);删除操作只会导致被删除元素的迭代器失效。
10、STL容器线程安全性
STL 容器本身不是线程安全的,读写混用一定要加锁。
面向对象
1、面向对象三大特征
继承、封装、多态
2、简述多态的实现原理
分为动态多态与静态多态。多态就是同一操作作用于不同对象,可以表现出不同的行为。
动态多态通过虚函数实现(virtual),属于运行时多态,运行时通过查找虚函数表实现动态多态。
静态多态主要通过函数重载、模板函数实现。
虚函数机制:编译器生成一个虚函数表(vtable),对象中多一个隐藏指针(vptr),指向该类的vtable。通过vptr找到vtable,在找到正确的函数地址。因此父类指针调用虚函数时,根据vptr找到实际指向对象类型调用对应函数。动态多态 = vptr + vtable + 虚函数
3、怎么解决菱形继承
通过多重继承导致同一个基类在最终派生类中出现多份,引发成员二义性和对象模型混乱。
首先菱形继承属于一个基类A,B、C继承A,D继承B、C形成的菱形继承。
一般使用虚继承来解决,例如在B、C继承A的地方加virtual。
4、函数重载和重写的区别?
重载(overload):同一个作用域,函数名相同,参数不同。静态多态
重写(override):父类与子类之间,函数一致,父类需要加virtual。动态多态
5、运行时多态实现原理
结合虚函数机制
6、虚函数调用过程
同上
7、继承下构造函数与析构函数执行顺序
执行顺序:父类(构造函数)子类(构造函数)子类(析构函数)父类(析构函数)
8、虚函数表和虚函数指针(vptr)的创建时机
虚函数表(vtable)在编译阶段,vptr在对象构造阶段由构造函数进行初始化。
构造时从基类 vtable 切到派生类,析构时从派生切回基类。
9、虚析构函数的作用?
虚析构函数让“父类指针指向子类对象”时,delete 时能够正确调用子类析构函数,防止资源泄漏。
10、智能指针种类以及使用场景
主要有三种:
- unique_ptr:独占型智能指针
- share_ptr:共享型智能指针(引用计数)
- weak_ptr:弱引用(避免循环引用)
11、C++11常用特性
auto、nullptr 空指针常量、智能指针移动语义、右值引用、时间库、枚举、static_assert、override / final 关键字、STL
C++11 的核心特性包括:
auto、range-for、nullptr、lambda、智能指针、
右值引用与移动语义、initializer_list、
std::thread、chrono、enum class、static_assert、
override/final、constexpr、decltype。
这些特性让 C++ 更安全、更高效、更现代化。
12、动态库与静态库的区别
静态库:.a结尾,window是.lib。
特点:链接时复制进可执行文件、可执行文件体积变大、更新库需重新编译整个程序、加载速度快。
优点:运行时不依赖外部文件,部署简单。启动更快。不会出现“库版本不一致”问题
缺点:可执行文件体积变大、库更新后需要重新编译所有依赖它的程序、占用内存较大
动态库:.so和.dll
特点:链接时不复制库代码,运行时动态加载。多个程序共享同一个库文件、库可以单独更新,不需要重新编译整个程序、加载时有额外开销
优点:节约内存、便于更新
缺点:发布时要带上 .so/.dll,部署更复杂、启动时需动态加载,速度略慢、库版本不匹配会导致“运行错误”
静态库:链接时复制到可执行文件中,不依赖外部库,体积大,更新麻烦。
动态库:运行时加载,不复制,多进程共享,占用内存小,便于更新,但运行依赖库文件。
13、右值引用与左值引用区别?右值引用的意义
右值:是一个将亡值,临时对象
左值:有名字、有地址。
右值引用的目的为了支持移动语义,避免不必要的拷贝,特别是临时对象、返回值优化、容器操作等场景。move。
左值引用绑定左值,右值引用绑定右值。
右值引用的意义是实现移动语义和完美转发,
避免临时对象拷贝,提高性能。
C/C++八股文面试核心考点
1328

被折叠的 条评论
为什么被折叠?



