一面 电话面,15分钟
问题
-
map和unorderedmap区别
map是红黑树实现的,unorderedmap是hash表实现的。 -
二叉树查找时间
log(n) -
树的遍历方式:
先序,中序,后序,层次 -
虚函数
虚函数的主要作用是实现了多态的机制。用父类指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。是一种泛型编程。虚函数是通过一张虚函数表来实现的。在表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证实例反应实际的函数。在有虚函数的类的实例中,这个表被分配到了实例的内存。虚函数表的指针存在于对象实例的最前面。 -
排序算法
- 稳定的排序:冒泡、归并
-
TCP和UDP
- TCP面向连接,可靠
- UDP无连接,不可靠
- 区别:
区别 TCP UDP 连接 面向连接 无连接 服务对象 点对点的两点间服务 支持一对一,一对多,多对一,多对多交互通信 可靠性 可靠交付,无差错,不丢失,不重复,按序到达 尽最大努力交付,不保证可靠 拥塞控制,流量控制 有 无 报文长度 动态报文长度 面向报文,不合并,不拆分 首部开销 20字节 8字节 适用场景 可靠但是速度慢 不可靠速度快 -
GET和POST
GET浏览器会把http header和data一起发送出去,服务器响应200返回数据。
POST浏览器首先发送header,服务器响应100continue,浏览器再发送数据data,服务器响应200(返回数据)- GET和POST区别
区别 | GET | POST |
---|---|---|
传参 | 通过url | 在request body中 |
长度限制 | url传参有长度限制 | 没有长度限制 |
安全性 | 更不安全 | 稍好 |
编码 | 只能进行url编码 | 支持多种编码 |
cache | 浏览器主动cache | 参数不会保留 |
记录 | 完整保留参数 | post参数不会被保留 |
TCP数据包数量 | 1个 | 2个 |
-
TCP建立和断开过程:
-
vector和list
- vector是顺序实现
- list是双向链表实现
-
C++内存模型
- 栈:局部变量,函数参数
- 堆:由new非配的内存块,delete释放
- 自由存储区:malloc等分配的内存块,用free来结束生命周期
- 全局区(静态区)全局变量和静态变量
- 常量存储区:存放常量,不允许修改。
-
常量指针和指针常量
- 常量指针:被指向的对象是常量,常量的指针。地址可变,值不能变
int const* p;const int* p;
- 指针常量:指针本身是常量。本质是个常量。指针常量的值是指针。
指针本身是常量,指向的地址不可以变化,但是指向的地址对应的内容可以变化。地址不可变,值可以变
int* const p;
- 常量指针:被指向的对象是常量,常量的指针。地址可变,值不能变
-
C++内存泄漏
指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存再物理上的消失,而是由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。- 内存泄漏分类:
- 堆内存泄漏:通过malloc,realloc,new等分配的堆内存使用完毕后没有用free和delete删掉。
- 系统资源泄漏:使用bitmap,handler,socket等没有用相应的函数释放掉,导致系统资源的浪费。
- 没有将基类的析构函数定义为虚函数:当基类指针指向子类对象时,如果基类的析构函数不是虚拟的,子类的析构函数就不会被调用,子类的资源没有正确释放。
- 如何处理内存泄漏:
- 使用varglind,mtrace检测。
- 内存泄漏分类:
-
new和malloc区别
new | malloc |
---|---|
按照数据类型分配 | 按照指定的大小分配 |
返回指定对象的指针 | 返回void* |
分配并调用构造初始化 | 只分配 |
用delete释放,且会调用析构 | 用free释放 |
new是操作符,可重载 | 是函数 |
没有这种操作 | 可用realloc扩容 |
分配失败报bad_malloc错误 | 返回NULL |
申请数组是 new[]一次分配所有内存,释放调用delete[] | 用sizeof(int)*n |
- 继承、虚继承和虚函数表对类的大小的影响
- 真空类
长度为1个字节。内存结构??每次都不一样class CNull { };
- 空类
长度为1,内存结构??内部的成员函数不会影响类大小class CNull2 { public: CNull2(){printf("Construct/n");} ~CNull2(){printf("Desctruct/n");} void Foo(){printf("Foo/n");} };
- 简单类
长度4 内存结构: 00 00 00 00 00class COneMember { public: COneMember(int iValue = 0){m_iOne = iValue;}; private: int m_iOne; };
只有成员数据才影响大小 - 虚继承
虚继承让长度增加4,多了一个指针
static不占用类的大小。
带虚函数的类长度增加4
总结
-
普通单继承,只需将自身成员变量的大小加上父类大小(父类中 有虚函数,子类中不管有没有)若父类没有虚函数,则子类大小需要加上指向虚表的指针大小。
-
普通多继承,若几个父类都有虚表,则子类与第一个父类公用一个虚表指针,其他有几个有虚函数的父类则就有几个虚表指针。
-
虚拟单继承,此时若子类有虚函数则加上一个自身的虚表指针的大小,(若没有则不加)再加上自身的成员变量大小,还要加上一个虚类指针ptr_sonclass_fatherclass,最后加上父类的大小。
-
多重虚拟继承,此时若子类有虚函数则加上一个自身的虚表指针的大小,(若没有则不叫)再加上自身的成员变量大小,还要加上 一个公用的虚类指针(不管有几个虚拟父类,只加一个),在加上所有父类的大小。
-
普通、虚拟混合多继承,此时子类的大小为自身大小(若子类或普通父类有虚函数,则为成员变量+虚表指针大小;若都没虚函数,则就为成员变量大小),加上一个虚类指针大小,在加上虚拟父类的大小,在加上普通父类的大小(除虚表指针,因为它和子类公用一个虚表指针)。
虚函数工作原理
- 虚函数的实现是通过对象携带额外的信息(虚函数表指针vptr)来实现的。vprt指向一个被称为vtbl的函数指针数组。
- 对象调用虚函数过程:找到对象的vptr指向的vtbl,然后再vtbl中寻找合适的函数指针。虚函数的地址翻译取决于对象的内存地址,而不是数据类型。
虚函数表是类级别的,vptr是指向虚函数指针数组是对象所有的。
介绍排序算法:
各种排序算法介绍
- 归并排序(稳定):该算法采用分治法;对于包含m个元素的待排序序列,将其看成m个长度为1的子序列。然后两两合归并,得到n/2个长度为2或者1的有序子序列;然后再两两归并,直到得到1个长度为m的有序序列。
- 冒泡排序(稳定):对于包含n个元素的带排序数组,重复遍历数组,首先比较第一个和第二个元素,若为逆序,则交换元素位置;然后比较第二个和第三个元素,重复上述过程。每次遍历会把当前前n-i个元素中的最大的元素移到n-i位置。遍历n次,完成排序。
- 快速排序(不稳定):通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
- 选择排序(不稳定):每次循环,选择当前无序数组中最小的那个元素,然后将其与无序数组的第一个元素交换位置,从而使有序数组元素加1,无序数组元素减1.初始时无序数组为空。
- 插入排序(稳定):对于一个带排序数组来说,其初始有序数组元素个数为1,然后从第二个元素,插入到有序数组中。对于每一次插入操作,从后往前遍历当前有序数组,如果当前元素大于要插入的元素,则后移一位;如果当前元素小于或等于要插入的元素,则将要插入的元素插入到当前元素的下一位中。
- 堆排序(不稳定):堆排序是一种选择排序,利用堆这种数据结构来完成选择。其算法思想是将带排序数据构造一个最大堆(升序)/最小堆(降序),然后将堆顶元素与待排序数组的最后一个元素交换位置,此时末尾元素就是最大/最小的值。然后将剩余n-1个元素重新构造成最大堆/最小堆。
时间复杂度
hash表的实现,包括STL中的哈希桶长度系数
hash表的实现主要包括构造哈希和处理哈希冲突两个方面
- 构造哈希:直接地址法,平方取中法,除留余数法等。
- 处理哈希冲突:开放定址法、再哈希法、链地址法、建立公共溢出区等。SGL中采用链地址法。SGL以质数来设计哈希桶长度。提前计算好28个质数。
- rehash:当loadFactor==1时,开辟一个原来桶数组的两倍空间,然后把原来的桶数组元素全部重新哈希到新的桶数组中。
二面 视频面,牛客
问题
- 内联函数和宏定义(凭自己理解写的)
- C++内存。new和malloc区别(一面的时候已经准备,所以答上来了)
- 链表找环,思路。求环外长度。
手撕代码
两个字符串,找最大公共子串
三面 视频面,牛客
问题
- 对一个超大的文件内容按第一列统计数量
- 排序
Answer:
with open(filename,'rb') as f:
for line in f: #自动使用缓冲以及内存管理
do something with the line
四面 HR面,牛客
略