第三次面试:C++实习开发
一、C++语法:
对封装继承多态的理解:
- 封装:将数据和对数据的操作方法包装在一起,对外提供接口,将接口实现细节隐藏起来,通过访问权限可以保证了数据的安全性。
- 继承:子类可以复用父类的代码,减少代码冗余,并且可以在父类的基础上拓展更多功能,大大增强了灵活性,但是耦合性比组合高,优先考虑使用组合。
- 多态: 子类在继承父类的基础上,还可以重写父类的虚函数,使不同的子类针对同一函数可以有不同操作,大大增强了代码的灵活性和可扩展性
二、数据结构:
vector和list的区别以及适用场景
特性 | vector | list |
存储方式 | 一段连续的内存空间 | 非连续存储 |
内存分配 | 提前申请好一大块连续的空间 | 按需申请,逐个节点申请 |
访问 | 支持随机访问,效率为O(1) | 不能随机访问,访问效率相较于顺序表式较低的,效率为O(N) |
插入 | 尾插效率高,但是头插以及随机位置插,涉及挪动元素,效率比链表效率低,O(N) | 插入节点时,只需要改变前后节点的链表关系,插入效率高,O(1) |
空间开销 | 小。仅需存储数据、容量、大小 | 大。每个节点都需两个指针,指向前节点和后节点 |
适用场景 | 1.需要频繁访问数据 2.存储数据量小,数据稳定 | 1.需要频繁插入删除数据 2.迭代器插入删除后依旧有效 |
不适用场景 | 1.需要大量的插入删除 2.没有很大的一段连续空间,不足以支持vector申请空间 | 1.需要频繁随机访问元素 2.内存紧张或对缓存性能要求极高,存储的对象本身很小 |
map和unoreded_map的底层区别,插入修改是如何进行的?
可以从它们的底层数据结构、时间复杂度和适用场景这三个方面来对比。
-
最核心的底层区别:
-
map
的底层是基于红黑树实现的一种自平衡二叉搜索树。这保证了元素在树中是根据键值有序排列的。 -
unordered_map
的底层是基于哈希表实现的。它通过一个数组和哈希函数来组织数据,所以元素是无序存储的。
-
-
这个区别带来的直接影响:
-
排序与遍历:
map
的迭代器会按键的顺序遍历元素,而unordered_map
的遍历顺序是不确定的。 -
时间复杂度:
-
在
map
中,因为红黑树是近似平衡的,所以它的查找、插入、删除操作的时间复杂度都是稳定的 O(log n)。 -
在
unordered_map
中,理想情况下(哈希冲突少),这些操作的平均时间复杂度是 O(1)。但在最坏情况(比如所有键都哈希到同一个桶,退化成链表)下,会退化到 O(n)。
-
-
-
关于插入和修改操作:
-
对于
map
:-
插入:当插入一个新键值对时,会从根节点开始,按照二叉搜索树的规则(比较键的大小)找到合适的插入位置(详细可以细看这篇文章:C++搜索神器:“搜索二叉树”-CSDN博客)。插入后,为了维持红黑树的平衡性质,可能需要进行树的旋转和重新着色。这个过程是 O(log n)。
-
修改:如果使用
key
已存在,它会通过 O(log n) 的查找定位到该节点,然后修改其值。如果使用insert
函数,对于已存在的键则不会修改。
-
-
对于
unordered_map
:-
插入:首先对键进行哈希计算得到哈希值后,再经过取模等操作定位到具体的桶。然后,在这个桶对应的链表中遍历,检查是否有重复的键。如果没有,则将新节点插入链表。当负载因子大于阈值时,就会扩容重新进行映射
-
修改:同样先通过哈希 O(1) 定位到桶,再在链表中遍历查找键。找到后修改其值。
-
-
总结一下如何选择:如果需要元素有序或者保证最坏情况下的性能,就用 map
。如果更追求平均情况下的极致访问速度,并且不关心顺序,那么 unordered_map
是更好的选择。
特性 | map | unordered_map |
---|---|---|
标准头文件 | #include <map> | #include <unordered_map> |
底层数据结构 | 红黑树 | 哈希表 |
元素排序 | 按键自动排序(默认升序) | 无序(取决于哈希函数) |
时间复杂度 | 查找、插入、删除:O(log n) | 平均查找、插入、删除:O(1) 最坏情况:O(n) |
三、计算机网络:
上传和下载文件的时候HTTP的请求行,响应行,传递文件的数据类型
文件上传:
-
请求行:使用的是POST方法,URL指向服务器处理上传的逻辑端点,例如
POST /upload HTTP/1.1
。 -
关键数据类型(请求头):
Content-Type
字段是application/octet-stream,将整个请求体直接作为原始二进制流发送。 -
响应行:如果上传成功,服务器通常会返回
200 OK
或者201 Created
。
文件下载
-
请求行:使用的是
GET
方法,URL直接指向要下载的文件资源,比如GET /download/1.txt HTTP/1.1
。 -
响应行:如果文件存在,服务器返回
200 OK
。 -
关键数据类型(响应头):
Content-Type
字段是application/octet-stream,将整个请求体直接作为原始二进制流发送。
四、操作系统:
多线程会有什么问题?如何解决?
多线程的核心问题源于对临界资源的线程安全问题
线程安全问题:这是最根本的问题。当多个线程在没有同步的情况下读写同一数据时,最终结果依赖于线程执行的随机时序,导致结果不可预测
解决方案:
-
使用是互斥锁,通过原子性确保同一时间只有一个线程能执行临界区代码。保证访问临界资源的唯一性(线程互斥)
-
使用条件变量,利用线程通信保证线程同步,保证多线程的线程按照一定的顺序进行操作(例如生产-消费者模型,只有当生产线程先生产,消费者线程才能进行消费)
五、反思:
1、对项目中使用的网络协议栈一定要熟,其底层协议,报头等原理都要做到张嘴就来
2、基础的数据结构(例如顺序表、链表、栈、队列等)底层都要熟悉,其构造函数,常用的成员函数要多敲几遍。对于高级的数据结构(像哈希表、红黑树等),他们常见的插入修改规则都要有一定的了解,越熟悉越好!!!
3、多线程的函数要多用,原生的API或者C++11封装的thread库都要自己在日常代码中多使用,理解地才更深刻
结语:
以上就是我第三次面试的全部内容了,希望对大家有些帮助,也希望与一样喜欢编程的朋友们共进步,希望大家面试全过,offer多多!!!
谢谢观看
如果觉得还阔以的话,三连一下,以后会持续更新的,我会加油的
祝大家早安午安晚安