快手8.17音视频开发凉面
凉经
- 5min自我介绍
- 看到有iOS项目,问为什么用Moya,相比于NSURLSession好在哪里?
- Moya封装了Alamofire,提供了一个抽象层,封装了网络层的细节,提供了更干净、更类似于Swift的API,使得封装网络请求编的更容易。
- NSURLSession是iOS自带的网络库,支持多种协议,可自定义程度更高。
- 两者封装性的差别:NSURLSession的扩展性会更高一些。
- C++ malloc 和 new的区别
- new分配内存失败时,会抛出bad_alloc异常,malloc分配失败时,只会返回NULL;
- new不需要显式地指示所需要的空间,而malloc需要显式指明;
- new会触发所创建对象的构造函数,而malloc不会;
- new是一个运算符,可以重载,而malloc是库函数;
- new所返回的指针类型一定是new的对象类型的指针,而malloc返回的是
void *
,需要再次转换为所需的类型的指针
- i++是原子性的吗?
- 不是原子性,虽然C++语句只有一句,但是实际上可以理解为做了三个操作:将i移动到寄存器中,在寄存器中做运算,再将运算后的结果拷贝到原先i的位置。
- 如果并发有多个线程进行i++操作,会造成数据的异常;
- 可以使用std::atomic来解决。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#include <bits/stdc++.h>
std::atomic<int> atomicCounter{0};
int counter = 0;
void atomicIncrement() {
for (int i = 0; i < 100000; ++i) {
++atomicCounter;
}
}
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter;
}
}
int main() {
std::thread atomicT1(atomicIncrement);
std::thread atomicT2(atomicIncrement);
atomicT1.join();
atomicT2.join();
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "atomic increment result: " << atomicCounter << std::endl;
std::cout << "Increment result: " << counter << std::endl;
return 0;
}
- SSL的过程
- 客户端发送
Client hello
, 其中包含客户端所支持的TLS版本, 加密套件, 以及随机数Client random
. - 服务器返回
Sever hello
, 其中包含服务器的TLS整数, 服务器所选择的加密套件, 以及服务器随机数Server random
. - 客户端对服务器提供的证书进行验证,
验证通过后发送
Client Key Exchange
消息, 一中包含了使用服务器证书公钥加密的一个随机数Premaster Secret
. - 服务器使用证书私钥解密得到
Premaster Secret
- 客户端和服务器都使用相同的加密套件, 根据
Client Random
,Server Random
和Premaster Secret
生成相同的密钥. - 客户端和服务器都相对方发送
Change Cipher Spec
以及Finished
消息, 表明自身已经计算好用于通信加密的密钥. - 握手完毕, 开始使用加密连接进行通讯.
- 客户端发送
- 介绍下死锁
- 死锁是指一组相互竞争资源的进程推进顺序不当而导致无法每个进程都无法推进下去的局面
- 在这一组进程中, 每个进程都占有并保持了一定的资源, 而又请求另外一些被其他进程占有的资源
- 触发条件: 互斥, 占有和等待, 不可抢占, 环路等待.
- 死锁预防: 破坏互斥(虚拟打印机技术), 破坏占有并等待(预先请求好运行过程中所需要的所有资源), 破坏不可抢占
- 死锁避免: 银行家算法
- 介绍下C++智能指针
- 分为shared_ptr, unique_ptr 和 weak_ptr
- shared_ptr在拷贝时使用同一份引用计数, 重载拷贝构造函数使得引用计数+1, 重载赋值运算符使得赋值前原来的对象的引用计数-1, 新的对象引用计数+1, 重载析构函数使得引用计数-1并判断是否需要delete
- 同一个shared_ptr被多个线程
读
是安全的, 被多个线程写
是不安全的(类比于i++的情况); 指向同一个实例的多个shared_ptr被不同的线程写
是安全的. - unique_ptr唯一拥有其所指的对象,离开作用域时, 对象会被销毁, 不支持普通的拷贝和赋值(但是一个将要被销毁的unique_ptr, 可以通过使用release或者reset将指针所有权转移到另一个unique_ptr上)
- weak_ptr的使用不会引起引用计数的变化, 主要是为了辅助shared_ptr工作, 使用前需要检查是否为空指针.
- 怎么样来避免菱形继承所带来的问题?
- 给定A为基类, B, C分别继承于A, D继承于B, C, 这种情况为菱形继承. 菱形继承会使得D中保留了两份基类A的数据, 分别存在于类B和类C当中, 会浪费空间, 且访问基类中的数据还要使用域运算符作区分.
- 使用虚继承来解决问题, 虚继承在声明派生类时指定继承方式, 在菱形继承中, 需要在定义类B, 类C时指明继承A的方式是虚继承.
- 虚基类的初始化由最后的派生类来负责初始化
- shared_ptr是否是线程安全的?
- 见7.
- C++类型转换,
static_cast
和dynamic_cast
statoc_cast
没有类型检查, 不能保证类型安全, 由派生类向基类转化是安全的, 但反方向是不安全的. 可用于基本数据类型之间的转化, 以及把任何类型的表达式转化为void类型dynamic_cast
是动态转化, 具有类型检查的功能, 互赞换后必须是类的指针, 引用或者是void*
, 基类要有虚函数, 可以交叉转化; 其只能用于存在虚函数的有继承关系的类的实例之间进行强制类型转换; 指针转换失败会返回nullptr, 引用转换失败则会抛出异常.reinterpret_cast
可以将整型转换为指针, 也可以把指针转化为数组, 可以在指针和引用里随意转换.const_cast
, 将常量指针转化为非常量指针, 常量引用转化为非常量引用
- C++怎么实现多态的?
- 重载(Overload)和覆盖(Override)
- 重载指允许出现多个重名函数, 且这些函数的参数表不同
- 覆盖指子类重新定义父类的函数的做法.
- map与unordered_map的区别
- map使用红黑树实现, key是有序的, 查找, 插入的时间复杂度为O(logn)且较为稳定
- unordered_map使用哈希表实现, key是无序的, 查找, 插入的时间复杂度通常为O(1), 但是不稳定, 根据哈希函数在有些情况下可能会达到O(n)的时间复杂度.
- TCP拥塞控制的流程
- 慢启动
- 拥塞避免(指数增长变为线性增长)
- 拥塞发生
- 超时重传--阈值设为原速率一半, 速率从1开始慢启动
- 快速重传--阈值和速率都设置为原速率一半.
- 快速恢复
- 操作系统虚拟地址到物理地址的转换流程
- CPU在TLB中找到虚拟地址的项, 就从该项中直接获取对应的物理地址
- TLB未命中, 则查找进程的页表, 如果其对应物理页面在内存中, 则更新TLB并检测访问是否合法, 通过之后给定物理地址
- TLB未命中, 且物理页面不在内存中, 则使用页面置换算法将该物理页换入内存中, 再次建立映射
- 在有1000+文件的一个目录里,给定一个字符串,怎么样去找到包含该字符串的那个文件
grep -nr "text" directory/
- Git提交中,如何避免环的产生
- 定期将主干上的更改合并到次要分支
- 使用
cherry-pick
选择性地将更改从一个分支应用到另一个分支而不会创建合并提交.
手撕的复原IP地址,但是ACM模式,紧张了。原本之前第一次做这题的时候自己就撕出来了,现在再做反而只把代码框架撕出来,甚至编译报错。
快手8.17音视频开发凉面