1.什么是大小端?如何判断大小端?
大端序列:高位字节存低位置,低位字节存高位置(就是正常生活中读的方向)
小端序列:高位字节存高位置,低位字节存低位置;
最常用的判断方法是:联合体
cpp
#include <iostream>
bool isLittleEndian() {
union {
uint32_t i;
char c[4];
} u = {0x01020304};
return u.c[0] == 0x04; // 低地址存储低位字节→小端
}
2.什么是函数重载 ?C++如何支持函数重载?
函数重载是允许在同一作用域内定义多个同名但参数列表不同的函数。C++ 通过 ** 名称修饰(Name Mangling)** 实现函数重载。编译器会根据函数名、参数类型和数量等信息,将函数名转换为唯一的内部标识符。
3.指针和引用的区别?
指针是存储变量的内存地址,可空,可重新赋值,需要显示解引用,适合动态的内存管理;
引用是变量的别名,必须初始化且不重新绑定,语法更加简洁,常用于函数传递和避免拷贝。
4.宏的缺点,什么可以替代?
·缺乏类型检查、副作用风险、代码可读性下降、命名冲突、调试困难、代码膨胀、递归展开问题;
可以考虑用inline、const 、enum等来替换。
5.nullptr的意义?
nullptr
是 C++11 引入的类型安全空指针字面量,解决了 NULL
的二义性问题(如整数 0 与指针的重载冲突),提高代码可读性和安全性。
6.面向对象和面向过程的区别?
面向过程是将程序视为一系列函数或过程的集合,以数据结构为中心,强调操作的步骤和顺序;
面向对象将数据和操作封装为了对象,通过继承和多态的形式实现代码复用和松耦合,以对象间的交互为核心;
7.class和struct的区别?
两者在语法上几乎等价,主要区别在于默认访问级别
8.默认成员函数有哪些?什么情况下要自己写?
默认成员函数一共有6个:构造、析构、拷贝构造、赋值、移动构造和移动赋值
一般当你构造都要自己写;深拷贝的类,要自己写析构、拷贝构造、赋值;深拷贝的类也需要实现移动构造和移动赋值;(需要有资源释放的类可以叫做深拷贝的类)
默认构造:简单说就是不传参的就是默认构造;
9.初始化列表的特性?哪些成员需要在初始化列表初始化?
特性:执行顺序、直接初始化避免其他开销;
const、引用、自定义类型成员;
10.malloc/free和new/delete的区别?
new
/delete
是 C++ 关键字,用于对象级内存管理:自动调用构造 / 析构函数,返回带类型指针,失败时抛异常。
malloc
/free
是 C 库函数,仅操作内存块:不调用构造 / 析构,返回 void*
需手动类型转换,失败时返回 NULL
。
11.什么是内存泄露?内存泄露的危害是什么?如何解决内存泄漏?
内存泄露是指程序动态分配的内存,在使用完,没有得到合理的释放;
危害:导致系统性能下降、持久占用系统内存、系统响应变慢、最终可能引发崩溃;
解决:优先使用智能指针,遵从RAII的原则;利用工具检测
12.什么是继承?继承的意义?
继承就是让子类复用父类;继承的意义在于通过代码复用、建立类的层次结构,并为多态提供基础,从而提升面向对象程序的可维护性与扩展性。
13.继承和组合?
继承是is-a关系组合是has-a的关系;组合的耦合度低,继承的耦合度高,优先使用组合;
14.什么是多态?
多态被分为静态多态和动态多态;
静态多态->函数重载;动态多态->在父子类中首先进行虚函数的重写,在通过父类指针或引用调用虚函数;此时指向父类调用父类虚函数,指向子类调用子类虚函数;
15.析构函数建议是虚函数?为什么?
主要是为了确保在通过父类指针删除子类对象时,能正确释放子类对象的资源 ,避免内存泄漏。
16.纯虚函数
包含纯虚函数的类叫做抽象类也叫接口类;抽象类不能实例化出对象;这一点也就间接的强制子类进行重写,如果不重写,那它还是抽象类;
17.实现继承和接口继承
普通函数的继承一般被称作实现继承;虚函数的继承一般被叫做接口继承;
18.重写\重载\隐藏的区别
重写就是子类重写父类的虚函数;重载就是在同一个作用域里面同名但参数不同;隐藏就是子类定义与父类同名但是参数不同或非虚的函数,从而达到屏蔽父类的这个函数;
19.多态的原理(指向谁调用谁)
·虚函数重写以后,父类对象虚函数表指针指向的虚函数表存的是父类的虚函数;
·虚函数重写以后,子类对象虚函数表指针指向的虚函数表存的是子类的虚函数;
也就是前面提到动态的多态里面说到的:父类指针或引用调用虚函数->取指向的对象虚表中查找对应的虚函数调用!
20.什么是左值引用?什么是右值引用?
左值引用:绑定到已存在的变量,就是我们常用的传参,避免拷贝构造 &
右值引用:绑定到临时值(右值) &&
核心区别:绑定的生命周期
右值引用的使用场景:深拷贝的类,传值返回的优化;深拷贝的类,做参数;
移动构造;移动赋值;完美转发
21.final和override
关键字 | 作用对象 | 核心功能 | 使用场景 |
---|---|---|---|
override |
派生类函数 | 强制验证重写基类虚函数 | 确保多态行为正确,避免隐藏或拼写错误 |
final |
基类虚函数或类 | 禁止重写虚函数或禁止类继承 | 锁定虚函数实现或密封类层次结构 |
22.熟悉C++11的哪些特性,分别讲讲?
1. 智能指针(Smart Pointers)
- 特性:自动管理动态内存,避免内存泄漏。
- 分类 :
std::unique_ptr
:独占所有权,禁止拷贝,支持移动语义。std::shared_ptr
:共享所有权,通过引用计数管理生命周期。std::weak_ptr
:弱引用,解决shared_ptr
的循环引用问题。
2. 右值引用与移动语义(Rvalue Reference & Move Semantics)
- 右值引用 :绑定到临时对象(右值),语法为
T&&
。 - 移动语义 :通过
std::move
将左值转换为右值,避免深拷贝。
3. Lambda 表达式
- 特性:匿名函数对象,简化回调函数和函数对象的编写。
- 语法 :
[捕获列表](参数列表) -> 返回类型 {函数体}
。
4.范围for、自动类型推导、初始化列表、线程库
23.什么是循环引用?
循环引用(Circular Reference) 是指两个或多个对象通过共享指针(std::shared_ptr
) 相互引用,形成一个闭环,导致引用计数无法降为零,最终造成内存泄漏。
24.四种类型转换
转换操作符 | 主要用途 | 检查时机 | 是否修改类型系统 | 风险 |
---|---|---|---|---|
static_cast |
基本类型转换、父子类转换 | 编译期 | 否 | 向下转换可能不安全 |
dynamic_cast |
安全的多态类型转换 | 运行期 | 否 | 性能开销(RTTI) |
const_cast |
去除 const /volatile 修饰 |
编译期 | 是 | 可能导致修改常量对象 |
reinterpret_cast |
底层指针 / 整数重新解释 | 无 | 是 | 完全绕过类型系统,极不安全 |
25.STL常见的容器
序列式容器是:string\vector\list\deque; 关联式容器:map\set\unordered_map\unordered_set
其中(set唯一,map唯一,但是mutimap一个k键值可以对应多个va)
容器适配器:stack、queue、priority_queue;
26. vector 和 list 的区别
- 底层实现:vector 是连续动态数组,内存连续;list 是双向链表,节点内存不连续。
- 用法 :vector 支持高效随机访问,用
[]
或at()
;list 只能迭代器顺序访问。 - 优缺点:vector 随机访问快,插入删除(尤其中间)慢且可能内存拷贝;list 插入删除快(已知位置),随机访问慢 。
27. vector 插入数据与扩容
- 插入数据 :
push_back
在尾部插入,时间复杂度平均 O (1) ;insert
可在指定位置插入,若在中间插入,需移动后续元素,时间复杂度 O (n) 。 - 扩容:当插入元素空间不足时,分配更大连续内存(一般为原容量 2 倍),拷贝原数据,释放旧内存,时间复杂度 O (n) 。
28. 解决 vector 和 list 缺点的方案及 deque 缺陷
- 解决方案:deque(双端队列)结合两者优点,支持高效首尾插入删除,也有一定随机访问能力 。
- deque 缺陷:内存非完全连续,迭代器实现复杂;中间插入删除效率低;内存管理相对复杂,有额外指针开销。
29. map 和 set 底层
- 底层结构:通常是红黑树(平衡二叉搜索树) 。
- 规则:左子树节点小于根节点,右子树节点大于根节点,且通过变色和旋转保持平衡 。
- 效率:插入、删除、查找平均时间复杂度 O (log n) 。
- 实现:节点包含键值(set 只存键)、颜色、父节点及左右子节点指针,通过特定规则维护平衡 。
30. map 的 operator []
- 功能:通过键访问值,若键不存在,自动插入默认值的键值对。
- 实现:在红黑树中查找键,找到返回对应值引用;未找到则插入键(值为默认值)并返回新插入值的引用 。
31. 类型做 map 和 set 的 key 的要求
需定义 <
运算符(或提供自定义比较仿函数),用于红黑树节点间比较大小,确定节点位置和树的结构 。
32. map、set 和 multi_xxx 的区别
- map/set:键唯一,map 存键值对,set 只存键 。
- multimap/multiset:键可重复,multimap 存键值对,multiset 只存键 。
33. unordered_map 和 unordered_set 实现
- 底层结构:哈希表,一般用链地址法解决哈希冲突,即每个哈希桶是链表(或现代实现中可能是红黑树等结构) 。
- 规则:通过哈希函数将键映射到哈希桶,相同哈希值的键(冲突时)存于同一桶 。
- 效率:平均插入、删除、查找时间复杂度 O (1) ,但哈希冲突严重时可能退化到 O (n) 。
- 实现:包含哈希表数组存储桶,哈希函数计算键的哈希值,通过链表或其他结构处理冲突 。
34. unordered_map 和 map 的区别
- 底层结构:unordered_map 是哈希表,map 是红黑树 。
- 元素顺序:unordered_map 无序,map 按键升序 。
- 效率:unordered_map 平均插入、删除、查找 O (1) ;map 平均 O (log n) ,但哈希冲突时 unordered_map 可能性能下降 。
- 适用场景:unordered_map 适合追求查找插入性能且无需顺序;map 适合需要有序遍历和范围查找 。
35. 类型做 unordered_map 和 unordered_set 的要求
需定义 ==
运算符(用于判断键是否相等),并提供哈希函数(将键映射为哈希值),可自定义或使用标准库针对常见类型的实现 。
37. 迭代器
- 类似广义指针,为容器元素遍历提供统一接口,支持如
++
、*
等操作,按功能分输入、输出、前向、双向、随机访问迭代器。
38. 仿函数
- 重载
operator()
的类或结构体,行为类似函数,可存储状态,用于定制算法(如sort
的比较规则)。
39. 六大组件关系
- 容器 :存储数据;迭代器 :连接容器与算法;算法 :操作容器元素;仿函数 :定制算法行为;适配器 :修改组件接口;分配器:管理容器内存。
- 分配器为容器提供内存,迭代器使算法可操作容器,适配器调整接口,仿函数定制算法。
40. 适配器
- 修改已有组件接口的工具,如容器适配器(
stack
、queue
)、迭代器适配器(reverse_iterator
)。
41. 迭代器失效
- 容器插入 / 删除操作可能使迭代器指向的元素或内存无效(如
vector
扩容、list
删除元素),使用失效迭代器导致未定义行为。
42. STL 优缺点
- 优点:高复用性、抽象程度高、性能优、可扩展。
- 缺点:学习成本高、模板错误信息复杂、部分场景性能欠佳。
- 设计亮点:组件松耦合,迭代器统一接口,仿函数定制算法。
- 设计不足:编译时间长,错误处理不友好。
43. 容器线程安全
- 非线程安全。多线程同时读写可能导致数据竞争或迭代器失效,需手动同步(如互斥锁)。
44.基于比较的排序算法
算法 | 最好时间 | 平均时间 | 最坏时间 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n²) | O(n²) | O(1) | 稳定 |
选择排序 | O(n²) | O(n²) | O(n²) | O(1) | 不稳定 |
插入排序 | O(n) | O(n²) | O(n²) | O(1) | 稳定 |
希尔排序 | O(n log n) | O(n^1.3) | O(n²) | O(1) | 不稳定 |
归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 |
快速排序 | O(n log n) | O(n log n) | O(n²) | O(log n) | 不稳定 |
堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 |
蒂姆排序 | O(n) | O(n log n) | O(n log n) | O(n) | 稳定 |
45.C++的优缺点有哪些?
优点:性能好,贴向底层
缺点:设计复杂,更新慢