这个博客系列会分为C++ STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新,基本涵盖了自己秋招历程被问过的面试内容(除了实习和学校项目相关的具体细节)。在知乎和牛客(某些文章上会附上内推码)也会同步更新,全网同号(lonely-stone或者lonely_stone)。
关于高频面试题和C++ STL面经,每次我会更新10个问题左右,每次更新过多,害怕大家可能看了就只记住其中几个点。(在个人秋招面试过程中,面试到后面,发现除了个人项目和实习经历外,个人所记录的内容基本能涵盖面试官能问到的)
(另外个人才疏学浅,如果所分享知识中出现错误,请大家指出,避免误导其他人)
百度2025校园招聘内推开始啦,快来投递你心仪的职位吧( 网申链接地址:https://talent.baidu.com/jobs/list?recommendCode=IZ1RTJ&recruitType=GRADUATE )填入内推码,完成投递,get内推绿色通道~我的内推码:IZ1RTJ
具体校招的定向部门推荐和社招都可以私信我进行系统推荐
1. static和const分别怎么用,类里面static和const可以同时修饰成员函数吗?
- static:当static修饰局部变量的时候,它就改变了局部变量的存储位置(从原来的栈中存放改为静态存储区)及其生命周期(离开局部变量仍在,只是无法访问,直到程序结束)。
- const:const修饰全局时,并未改变上述的位置和生命周期,只改变了作用域,使当前文件外的源文件无法访问该变量。
- 类里面static和const不可以同时修饰成员函数
2. static关键字的作用?为什么类的静态成员函数不能访问非静态成员变量?
在类中使用 static 关键字修饰成员变量,表示该成员变量是类的静态成员变量(static member variable),它属于整个类,而不是某个对象。类的静态成员变量存储在静态存储区(static storage area)中,生存周期与整个程序相同。类的静态成员变量可以通过类名访问。
在类中使用 static 关键字修饰成员函数,表示该成员函数是类的静态成员函数(static member function),它属于整个类,而不是某个对象。类的静态成员函数可以通过类名访问,并且不能访问类的非静态成员变量。
为什么类的静态成员函数不能访问非静态成员变量呢?这是因为类的静态成员函数属于整个类,而不属于某个对象。而非静态成员变量属于某个对象,必须通过对象的实例访问。因此,类的静态成员函数无法访问非静态成员变量,因为它没有某个具体的对象来访问。
3. C++中的inline用法
- 作用:inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。
- 对于内联函数,C++有可能直接用函数体代码来替代对函数的调用,这一过程称为函数体的内联展开。
- 对于只有几条语句的小函数来说,与函数的调用、返回有关的准备和收尾工作的代码往往比函数体本身的代码要大得多。因此,对于这类简单的、使用频繁的小函数,将之说明为内联函数可提高运行效率
- 以下情况不宜使用内联:
- 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
- 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
4. define宏定义和const的区别
- 编译阶段:define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。
- 安全性:
- define只做替换,不做类型检查和计算,也不求解,容易产生错误,一般最好加上一个大括号包含住全部的内容,要不然很容易出错。
- const常量有数据类型,编译器可以对其进行类型安全检查
- 内存占用:
- define只是将宏名称进行替换,在内存中会产生多份相同的备份。const在程序运行中只有一份备份,且可以执行常量折叠,能将复杂的表达式计算出结果放入常量表
- 宏替换发生在编译阶段之前,属于文本插入替换;const作用在编译过程中。
- 宏定义的数据没有分配内存空间,只是插入替换掉;const定义的变量只是值不能改变,但要分配内存空间
5. vector、map、multimap、unordered_map、unordered_multimap的底层数据结构,以及几种map容器如何选择?
- 底层数据结构:Vector基于数组,map和multimap基于红黑树,unordered_map和unordered_multimap基于哈希表。
- 根据应用场景进行选择:
- Map/unordered_map不允许重复元素
- Multimap/unordered_multimap允许重复元素
- Map/multimap底层基于红黑树,元素自动有序,且插入和删除效率高
- Unordered_map/unordered_multimap底层基于哈希表,故元素无序,查找效率高
- 红黑树
- 一种二叉查找树,但在每个节点上增加一个存储位表示节点的颜色,red或者black。其查找插入和删除的时间复杂度最坏为O(log n);因为树的高度无限接近log n,最多为2log(n+1)
- 根节点是黑色,叶节点是不存储数据的黑色空节点。
- 任何父子关系的节点都不能同时为红色
- 任意节点到其可到达的叶节点之间包含相同数量的黑色节点。
- 红黑树相对于哈希表,在选择使用的时候有什么依据?
- 权衡三个因素:查找速度,数据量,内存使用和可扩展性
- Hash速度比map快,而且查找速度与数据量大小无关,属于常数级别;而map的速度是log(n)级别,并不一定比常数大,所以hash有hash的耗时。当数量较多且考虑效率时选择hash,但是当要求少消耗内存时,就得考虑map了
6. list的底层原理
list的底层是一个双向链表,使用链表存储数据,并不会将他们存储到一整块连续的内存空间中
各元素占用的存储空间(节点)是独立的,分散的,他们之间的线性关系通过指针来维持,每次插入或删除一个元素,就配置和释放一个元素空间。
List不支持随机存取
7. 什么情况下用vector,什么情况下用list,什么时候用deque
- Vector可以随机存取元素,但在非尾部插入删除时,效率低,适合对象简单,对象数量变化不大,随机访问频繁的。除非必要,不然尽量选用vector而非deque,因为deque的迭代器比vector复杂的多。
- List不支持随机存取,适用于对象大,数量变化频繁,插入和删除频繁,比如写多读少。
- 需要从首尾两端急性插入或删除操作的选用deque
8. set的底层实现为什么不用哈希表而使用红黑树?
- set中元素是经过排序的,红黑树也是有序的,而哈希是无序的。
- 如果只是单纯的查找元素的话,那么肯定选哈希表,因为哈希的最好查找时间复杂度为O(1),并且如果用到set中,那么查找时间复杂度一直就是O(1)了,因为set中是不允许有元素重复的。而红黑树查找时间为O(logn)
9. Priority_queue的底层原理
优先队列:其底层使用堆来实现的。在优先队列里面,队首元素一定是当前队列中优先级最高的那个。
10. 迭代器失效的问题插入
操作:
- 对于vector和string,如果容器内存被重新分配,iterations,pointers,references会失效;如果没有重新分配,那么插入点之前的iterator有效,之后的无效
- 对于deque,如果插入点位于除front和back外的其他位置,iterations,pointers,references失效;插入元素是到front和back时,deque的迭代器失效,但reference和pointers有效
- 对于list和forward_list,所有的iteration,pointers,references都有效
删除操作:
- 对于vector和string,删除点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的。
- 对于deque,如果删除点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,off-the-end失效,其他的iterators,pointers,references有效
- 对于list和forward_list,所有的iterator,pointer和refercnce有效。
- 对于关联容器map来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用,否则会导致程序无定义的行为
11. STL线程不安全的情况
- 在对同一个容器进行多线程的读写、写操作时
- 在每次调用容器的成员函数期间都要锁定该容器
- 在每个容器返回的迭代器(例如begin或end)的生存期内都要锁定该容器
- 在每个在容器上调用的算法执行期间锁定该容器
12. c++智能指针std::shared_ptr
- 智能指针主要的用途就是方便资源的管理,自动释放没有指针引用的资源。
- 使用引用技术来标识是否有多余指针指向该资源(shared_ptr 本身也会占一个计数引用
- 可以使用shared_ptr.use_count() 来查看引用技术,一旦为0,就会销毁
- make_shared要优于new,它可以一次性将需要的内存分配好。std::shared_ptr p = std::make_shared(),std::shared_ptr p(new …) , 不能直接std::shared_ptr p = p1,这种是不行的。
- 它的大小是普通指针的两倍,因为包含一个原始指针指向数据,还有指向引用计数的指针。
- weak_ptr 类型指针只能访问所指的堆内存,而无法修改它
12补 c++智能指针总结版
智能指针不是指针,是一个管理指针的类,用来存储指向动态分配(堆)对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。
智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
C++11中提供了三种智能指针,使用这些智能指针时需要引用头文件
13. c++11新特性
- to关键字:编译器可以根据初始值自动推导出类型,但是不能用于函数传参以及数组类。
- llptr关键字:nullptr是一种特殊类型的字面值,可以被转换成任意其他的指针类型,而以前的NULL只能被宏定义为0,在遇到重载时可能会出现问题。
- 智能指针:新增了std::share_ptr、std::weak_ptr等类型的智能指针,用于解决内存管理的问题。
- 初始化列表:可以使用初始化列表来对类进行初始化
这种问题也挺常见的,后续可能会问C++14和17新特性什么的