腾讯一面面经
1.vector相关
Q:vector clear操作会不会释放内存
手动试了一下,delete和clear操作都不会释放内存。vector的内存释放实在vector析构的时候进行的,即使vector元素全部被delete,其占据的内存空间还是保留。vector的内存会根据插入元素自动生长,但不会回收内存。
Q:vector erase操作注意事项
vector进行erase操作,例如以下代码,删除所有元素,存在以下问题
vector<int> vi;
for(auto iter=vi.begin();iter!=vi.end();iter++)
{
vi.erase(iter);
}
1.返回的是所删除位置的下一个位置的指针,iter本身变为野指针。此时不能对iter进行操作,因此上面代码会报错。
改为: iter=vi.erase(iter);
2.上述代码还存在一个问题:返回的iter指向删除位置的下一个位置,此时iter不能再进行++操作;
可以改为:
vector<int> vi;
for(auto iter=vi.begin();iter!=vi.end();)
{
iter=vi.erase(iter);
}
3.另外注意在iter等于vi.end()时,不能进行++或–操作
还有常问的vector存储、内存增长
2.C++多态相关
Q:C++多态的体现
C++多态有编译期多态和运行期多态
编译期多态:通过函数的重载实现
运行期多态:通过虚函数机制实现,体现在动态绑定上。简要的说,一个基类指针指向一个派生类对象,通过基类指针调用方法实际上是调用派生类的方法,这种调用在运行时才能确定。
Q:虚函数表的实现原理
参考 虚函数表的理解
虚函数表存储一个类的虚函数的地址,包括类本身以及父类(所有继承的类)的虚函数的地址。如果子类重写了父类的虚函数,在虚函数表中会把对应的虚函数地址替换为子类的函数的地址。
每个类都有一个虚函数指针,指向虚函数表。虚函数表指针放在对象的开始地址处,指向对象所在类的虚函数表的地址。在多继承情况下(第一个基类作为主类),会存在多个虚函数表指针,分别指向对应不同基类的虚函数表。
Q:虚函数表的存放位置,原因
回答的时候面试官提示了一下(考虑静态变量的存储位置?)
好多博客都涉及到这个话题,应该是存放在静态区。虚函数表是一个类所共有的,不属于某个对象。
c/c++程序所占用的内存一共分为五种::堆区、栈区、程序代码区、静态区、文字常量区
3.排序算法相关
Q:各种排序算法(所有能记得的)的时间复杂度和基本实现原理(重点问了快排和堆排序)
平均复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定 | |
---|---|---|---|---|---|
冒泡法 | o(n2) | o(n)正序 | o(n2)逆序 | o(1) | 稳定 |
简单选择排序 | o(n2) | o(n2) | o(n2) | o(1) | 稳定 |
直接插入排序 | o(n2) | o(n)正序 | o(n2)逆序 | o(1) | 稳定 |
希尔排序 | o(nlogn)~o(n2) | o(n1.3) | o(n2) | o(1) | 不稳定 |
堆排序 | o(nlogn) | o(nlogn) | o(nlogn) | o(1) | 不稳定 |
归并排序 | o(nlogn) | o(nlogn) | o(nlogn) | o(n+logn) | 稳定 |
快速排序 | o(nlogn) | o(nlogn) | o(n2) | o(logn)~o(n) | 不稳定 |
堆排序和快速排序过程以及最好最坏情况的分析,多写几遍代码吧
4.智能指针
Q:智能指针原理及其特点
智能指针的引入主要是为了解决内存泄露问题,new的对象,往往容易出现忘记delete或者程序未执行到delete语句就退出类似的情况,智能指针实际上也是一种对象,它管理所指向的对象,智能指针析构时,删除所管理的对象,释放内存。
auto_ptr(C++11之前的):已被C++11弃用
auto_ptr拥有所管理的对象,对auto_ptr进行赋值时,实际上是转移对象的管理权,例如p1=p2,此时p2为空指针,p1拥有p2原有的对象,如果p1原先不为空,则释放原先的资源
unique_ptr
unique_ptr拥有所管理的对象,不能进行拷贝复制和复制构造,但提供move操作(移动语义),通过move可以实现移动构造和移动赋值,即将对象的管理权移交给另一个unique_ptr,原先的unique_ptr失效。同时提供swap函数,交换两个unique_ptr对应的对象的管理权。
shared_ptr:
多个shared_ptr可指向同一个对象,内部计数器记录当前指向该对象的shared_ptr的个数,当计数器为0,则释放所管理的资源。
weak_ptr:
weak_ptr可以看做shared_ptr的辅助指针,只能通过shared_ptr获得对对象的管理权,绑定到shared_ptr上,不能直接访问对象。weak_ptr不参与shared_ptr的内部计数。和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。不能通过weak_ptr调用类的方法。
Q:weak_ptr使用场景,为什么要设计weak_ptr
weak_ptr不参与计数,可以通过weak_ptr获取shared_ptr是否有效。
但实际上weak_ptr主要是为了解决shared_ptr相互引用造成的死锁问题,两个类中shared_ptr相互引用的情况,如下:
Q:A中shared_ptr指向B, B中shared_ptr指向A造成什么问题
两个shared_ptr相互引用,如题所述的情况,那么这两个shared_ptr的引用计数最小是1,永远不可能下降为0,资源就不会被释放。
class B;
class A
{
public:
shared_ptr<B> pa_b;
A()
{
cout << "A Constructor" << endl;
}
~A()
{
cout << "A Destructor" << endl;
}
};
class B
{
public:
shared_ptr<A> pb_a;
B()
{
cout << "B Constructor" << endl;
}
~B()
{
cout << "B Destructor" << endl;
}
};
void shared_A_B()
{
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
cout << "pa 计数:" << pa.use_count() << endl;
cout << "pb 计数:" << pb.use_count() << endl;
pa->pa_b = pb;
pb->pb_a = pa;
cout << "pa 计数:" << pa.use_count() << endl;
cout << "pb 计数:" << pb.use_count() << endl;
}
int mian()
{
shatred_A_B();
return 0;
}
程序输出:
shared_A_B函数退出,两个shared_ptr计数仍为1,因此两个对象不会被析构
5.new、delete问题
Q:使用new和delete应该注意什么问题
(1)使用new分配内存而忘记使用delete释放内存的情况,造成内存泄露问题。C++中使用智能指针模板类来管理动态内存,以避免忘记delete而造成内存泄漏的问题。(具体见第4个问题)
(2)使用new分配内存,容易出现越界写入堆的情况,例如:
int* num = new int[10];
for (int i = 0; i < 11; i++)
{
num[i] = i;//此处的越界写入可以成功
}
delete[] num;//不能正确释放内存,程序中断
会出现以下问题:
这里的越界写入可以成功,但因为操作了不正确的内存,导致释放内存中断
(3)new[]数组和delete[]搭配,以保证正确释放内存
在 new [] 一个对象数组时,需要保存数组的维度,C++ 的做法是在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete [] 时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。
参考 new/delete详解
6.map相关
Q: map底层实现是什么结构
STL的map底层是用红黑树实现的,查找时间复杂度是log(n);hash_map底层是用hash表(以空间换时间)存储的,查询时间复杂度是O(1);
(map是按照键值进行升序排序的)
Q:红黑树插入删除复杂度
红黑树以O(log(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。
其他的常考的map等相关结构的比较
7.B+树、B树
Q:B+树和B树的区别,为什么要有B+树和B树
Q:文件系统为什么用B+树,不用B树或者红黑树
8.TCP、UDP相关
Q:TCP与UDP区别:
TCP是传输控制协议,提供面向链接的、可靠的字节流服务。TCP提供超时重发、丢弃重复数据、检验数据、流量控制等功能。
UDP是用户数据报协议,是一个简单的面向数据报的传输层协议,不会建立链接,不保证可靠性,只负责发送数据报,不负责对方是否收到,没有超时重发、流量控制等机制,传输速度快,但不可靠。
Q:TCP四次挥手
Q:TCP保证可靠性的机制
参考 可靠性保证机制总结
参考 TCP如何实现可靠性,及提高性能
Q:服务端忙时TCP有什么机制
(缓冲区满,主要是流量控制机制)
9.进程与线程
Q:进程间通信方式
Q:进程和线程区别
Q:线程独占的资源有哪些
线程共享进程的资源。但每个线程拥有独占的堆栈,每个线程的堆栈有一帧,工各个被调用但还没有从中返回的过程使用,该堆栈中存放了相应过程的局部变量以及过程调用完成之后使用的返回地址。
进程 | 线程 |
---|---|
地址空间 | 程序计数器 |
全局变量 | 寄存器 |
打开文件 | 堆栈 |
子进程 | 状态 |
即将发生的定时器 | |
信号与信息处理程序 | |
账户信息 |
10.操作系统相关
Q:用户态、内核态的区别,为什么要设计用户态和内核态
Q:切回内核态的场景
系统调用
异常
外部中断
Q:资源的最小分配单位
进程;进程用于把资源集中到一起,而线程是在CPU上被调度执行的实体;即资源的分配单位是进程,调度单位是线程。
Q:局部变量保存在哪里,堆栈都保存什么数据
11.算法相关
Q:如何用两个栈模拟队列实现
剑指offer原题
Q:海量数据如何找到最大的k个数(堆)
格外注意,最大的K个数,用最小堆;最小的k个数,用最大堆
https://blog.csdn.net/hanjing_1995/article/details/51539593
https://blog.csdn.net/qq1404510094/article/details/80323168