虎牙C++后台开发实习面经(已拿offer)
985硕士,目前找暑期实习中,学校与虎牙同城,在offershow上查了一下待遇也不错,于是3月初投了简历,之前一直是用人部门筛选中,最近才约上了面试。
发现牛客网上虎牙的面经不多,于是贡献一篇,争取能走完三轮技术面
3.31一面,1h,电话面试
(以下回答中Q为面试官,A为我,派生类=子类,父类=基类,有些没有写回答的问题是回答上来了,并且扯了很多,因为不是很难,就没有具体记录我的回答了)
自我介绍+项目
看过什么C++书
后台开发是什么,与项目关系强吗(最后面试官说现在互联网公司不缺钱,买了大量的硬件,不需要人力那么强,可能跟我想的后台开发不太一样)
extern是什么---extern在变量前、extern "C"在函数前
Q:extern "C"在什么场景下使用
A:不需要虚函数重载的情况
Q:这是为了用而用,有没有不得不用的场景
A:只支持C编译器的开发环境下?
Q:那根本没法编译cpp了嘛,其实答得差不多了,当C++要作为库文件给C去调用时,不得不用
重载与覆盖有什么区别---重载的定义、覆盖的定义
Q:如何调用非虚的重载过的父类函数
A:static_cast可以吗
Q:emmm 这是一种方法吧,怎么样避免覆盖呢?
A:C++11支持的final关键字
Q:emmm final是避免虚函数进行重写吧?你可能没理解我的意思,父类有个非虚函数,我想在子类实现一个同名函数,但又不想覆盖父类,我想实现一种重载的机制
A:那我把这个返回值修改一下可以吗
Q:恐怕没有关系吧。emmm没事,想不起来就算了,这是比较生僻的语法。
析构函数为什么要声明为虚函数---内存泄漏
Q:在什么场景下没法析构呢
A:定义指向父类的指针,但是指向派生类,如果析构函数不是虚的,那么delete这个指针时,派生类的独有资源无法析构
Q:什么情况下派生类没有多余/独有资源
A:派生类没有自己定义新的数据成员,又没有重写虚函数
Q:如果重写了父类的虚函数,就会有多余的资源吗
A:对,因为重写虚函数,虚函数表要更新,而且派生类对象有一个虚函数表指针
Q:如果子类没有自己定义新的数据成员,只是重写了一个虚函数,那么它的内存体积会比父类大吗
A:emmm 应该是一样的,数据成员是一样的,非虚函数不占用类对象的内存空间,而父类与子类都有一个虚函数表指针,也是一样的
Q:如果在父类的虚析构函数中调用了虚函数,子类重写了这个虚函数,那在析构时,父类的析构函数中执行的虚函数是父类的还是子类的?为什么?
A:父类的,一般来说,在析构函数不应该调用虚函数,因为没法体现多态性,在析构时,顺序是先析构子类再析构父类,所以当执行基类的析构函数时,子类已经析构完了,所以虚函数只能是父类实体的
Q:子类没有重写父类的函数,但子类有自己的一些内置类型,如果父类的析构函数不是虚函数,那会产生内存泄漏吗
A:应该不会,巴拉巴拉(乱扯,什么堆空间)
Q:(拉回主线)我要delete一个类,类中的成员变量都是在堆上的,你再想想
A:(沉默了10s)我总觉得不会产生内存泄漏,可能是因为有什么内存回收的机制吧
Q:OK,那分析一下delete的过程
A:先析构,再回收空间
Q:用哪个函数回收呢?
A:delete operator - operator delete - C的free函数,牵涉到底层内存管理,
Q:free要指定长度吗
A:不用
Q:这是不是就是上一个问题的答案呢?
A:(恍然大悟)哦~所以这些成员变量相当于向前在类里面,在free类对象的时候也回收掉了
vector与list的区别---从连续内存与非连续内存的角度分析了一下性能
Q:vector当capacity不够时,需要重写申请空间-copy-销毁旧空间,这个时间复杂度是多少
A:单这个行为是O(n)
Q:那为什么push_back是O(1)呢
A:摊还分析,前面经历了很多次O(1)的push_back,这次O(n)push_back可以把时间摊还到前面去,所以整体是O(1)
Q:如果出现扩容,原本迭代器会失效吗,为什么?
A:vector迭代器底层是原生指针,指向原来的地址,扩容后需要搬运,所以原来的地址失效了
Q:那如果要你重新设计vector的话,要怎么样才能让迭代器不失效呢
A:迭代器中除了原生指针,还需要加入相对于begin()的偏移量
STL map底层是啥,为啥选红黑树不选AVL树,AVL树插入和删除最坏情况(O(n)),为什么不用B+树
Q:你说的B+树在数据库场景下能减少IO次数,应该指的是能减少IO次数,那如果是SSD有什么不同
A:(我的天,这是啥问题)这个。。。不太清楚
Q:那换个问题,SSD与机械硬盘的优缺点
A:(不是很清楚SSD的原理)SSD好像是什么二极管,有很多格子,用电激活起来,比较脆弱,随机读写性能更好。机械硬盘的随机读写性能比较差,因为它是有磁头的,磁头要去寻道,这牵涉到很多寻道算法,比如电梯算法,但是还是要移动磁头,所以性能较差巴拉巴拉
Q:emmm 差不多吧,不是因为SSD太强,而是因为对手太弱了
STL内存配置器有重写过吗(没有)
进程与线程的区别,线程私有变量是什么(回答不清楚,面试官说这是很关键的一个点,复盘时我才听清是thread local storage线程局部存储,不过我也不会)
多线程与单线程的优劣
STL容器是线程安全的吗(不是),有什么需要注意的(加锁与同步)
Q:多个线程读vector的内容需要加锁吗
A:不需要
Q:如果很多线程读,一个线程写,用什么锁比较好
A:读写锁吧
Q:读写锁有两种设计方式,一种是写优先、一种是读优先,写优先怎么做
A:写的时候,加锁,排斥其他的读
Q:读优先呢?
A:多个线程都能读,一个线程写的时候加锁
Q:emmm 读写锁本来就是要这样的(沉默了一会)OK吧,下一道题
C++11的atomic有听过吗(有),有几种内存模型(不知道)
服务端用accept,成功返回,说明三次握手完成了几次
A:(没搞懂他的问题,僵了好久)三次吧
Q:emmm ok,如果已经建立连接,那客户端如果连续发三次100字节的TCP包,那服务端有哪几种可能性呢
A:首先看不需要分片,100字节小于互联网场景的MTU,不需要分片,如果在不丢包的情况下,会收到三个包
Q:有没有可能收到一个300字节的大包
A:有可能,TCP是面向字节流的,服务端不知道消息的长度,在接收缓冲区读一定长度的字节,所以需要做粘包的处理
回到项目,华为比赛,可以用分布式吗,考虑一下
有写过分布式程序吗(没有),有了解分布式吗(知道CAP定理),现实中选择CAP中的哪项丢弃(A不能,CP看情况),什么情况下把P舍弃掉(我听错了,回答NoSQL把P舍弃掉了,复盘时才知道答非所问,面试官没说什么)
总结:很多问题都是从一个点出发,往深处挖,虽然有一些答不出来,但是还是挺有收获的。
4.2二面,45min,牛客视频
项目
STL容器有哪些(按照STL源码剖析的顺序讲了一遍)
Q:红黑树和哈希表,什么场景用呢
A:有序无序,时间复杂度(查找、插入/删除)、哈希表内存更大一点(一般是数据总量N*2,而且是质数)
Q:既然提到了内存,epoll里面是啥数据结构,为什么呢
A:红黑树,epoll_ctrl需要经常插入或删除,红黑树可以以更稳定的Ologn时间复杂度,哈希表在不断插入时可能需要rehash,在不断删除时原先开辟2N空间又会大大浪费
Q:emm这是内存的消耗,那我举个例子,假设TCP连接有十万个,内存消耗有多少呢?
A:单单一个TCP连接只消耗一个文件描述符fd,一个TCP连接有接收缓冲区与发送缓冲区,内存单位可能是K,取决于具体实现
Q:emm 是的,内存这也是一个点,但是在小场景下,哈希表浪费的内存也没有太大,有没有更重要的关注点
A:在时间上,哈希表可能需要rehash,红黑树在插入和删除后,最多两次还是三次就可以达到平衡,更加稳定
Q:emm 还有没有其他的点,可以影响到你红黑树与哈希表的选型呢
A:emmm
Q:工程上就是不断地取舍,再想想
A:(一番思考)我想到了一种场景,类似于数据库要用B+树的原因,要进入哈希表的数据需要经过哈希函数,可能会散落在哈希表各个地方,没有规律;红黑树是一种有序结果,相似的项目就在附近,所以遍历查找会更快一点
Q:(分析了一通)不是很确定,回头可以验证一下。其实还有个很重要的点,你没想到,那就是重载的问题,可能这个数据根本无法比大小
A:哦~嗯。。。那这个可能也算在有序无序的区别里面?
Q:也就是operator < 压根不能使用,这个时候不能用红黑树
A:哦~对对对,有道理
进程与线程的区别(巴拉巴拉,还说了一面完后学的thread local storage)
Q:(当我说到线程有自己独有的栈段与寄存器时,打断)为什么线程会有独有的寄存器,放在那里?
A:噢噢,我说的是程序计数寄存器
Q:emm 那放在哪里呢,比如我开一万个线程,就有一万个寄存器跑出来?
A:CPU里面吧,可能是L1 L2缓存里面,当我要调度下一个线程,就从缓存中把相应的程序计数寄存器读取出来(我的说法容易引起歧义,寄存器是一个个物理的、真实存在的元件)
Q:(解释了一通)寄存器应该是轮流占坑的东西
A:是是是是,我是这个意思
Q:有听说过协程吗
A:交织式的子程序,C++20,微信开源了协程库libco巴拉巴拉,具体怎么实现与应用场景坦白不是很清楚
Q:你说到了C++20,你对C++的新标准有什么了解呢
A:C++20有个Ranges,是对于区间范围的升华,好像传入泛型算法里的参数可以有更多的范围形式,巴拉巴拉,具体有点忘了
Q:如果想做左开右闭怎么办
A:把begin和end往前移一下
Q:ok 这不是很重要的事情
Q:条件变量为什么要传入一个锁,为什么不放在内部呢,就像printf一样,对缓冲区加锁解锁,用户不需要关心
(一来一回聊了很久,我始终没有答到点上,但是竭尽全力把自己的知识都吐出来了)
TCP与UDP的区别(我讲了很多很多,聊到自己搭建服务器用了bbr拥塞控制算法什么的)
Q:DNS为什么用UDP
A:包更多、对网络消耗更大、时间更多
Q:http dns有听过吗,就是用http来实现dns
A:(刚开始没听懂,解释了一番dns)哦。。是用http来做dns,没听过
Q:P2P,你觉得用TCP和UDP
A:我想想,点对点,我觉得是TCP吧,因为传输很大的字节流,要保证完整,所以我觉得是用TCP做的
Q:但是其实是用UDP做的
A:(尴尬)啊这样子的嘛。。(傻笑)
虎牙直播,弹幕的字数是1-30个字,后台有100w个敏感词数据库,怎么样实现屏蔽效果,注意是包含,可能有几个字是敏感词,也需要屏蔽
A:分词巴拉巴拉,nlp巴拉巴拉,布隆过滤器巴拉巴拉,哈希表巴拉巴拉
Q:分词属于nlp范畴了,感觉有点太过了,我想问的是nlp的一个子问题,而且我想要的是包含逻辑,不是匹配逻辑(特指布隆过滤器)
A:kmp算法?巴拉巴拉
Q:那得运行很多遍,不用这么复杂
A:(想了一会)好像在搜索引擎中,可以联想关键词,好像用到了一种叫做什么trie树来着
Q:那怎么具体怎么解决的呢
A:emm 具体有点不太了解
Q:ok我没什么问题了,你有什么问题可以问我
A:我想问一下刚才这道题,您最期待的答案是什么
Q:emm 这有很多种解法,有挫一点的也有高大上一点,比如AC自动机,树,或者自己构造一个树,trie树什么的
A:那虎牙直播是怎么解决的呢
Q:最开始我写的一个版本是用到了类似的,后来用了AI,现在还支持拼音,还有更多的组合,你可以自己写一写,挺有意思的
A:您觉得我还有什么提高的吗
Q:基础还可以,有些点没答上来(觉得我p2p那里没回答出来有点可惜),其他的还好
总结:面试官很友好,整个面试像一场技术探讨,很多东西我可以自由发挥,我说的一些点,面试官也在思考,感觉不错
半小时后HR电话,通过二面啦
## 虎牙三面
4.3 电话 15min
为了找C++后台开发岗位,你做了哪些准备
项目做了什么,有什么收获(每个项目都聊了一会)
网络编程了解多少(看了UNP、APUE,还看了陈硕大神写的Muduo网络库,但由于时间关系没来得及自己造轮子,巴拉巴拉)
半小时后HR电话,通过三面啦
## 虎牙HR面
4.3 电话 15min
为啥投虎牙,啥时候看虎牙直播的,对虎牙有其他了解吗
有投其他公司的吗,怎么抉择
啥时候能来实习