【面经】C++面筋记录 (一) 231021
记录部分从牛客网上看到的面筋
1.声明一个vector,当vector过大时会栈溢出吗?
答案是会。
默认申请的vector是放在栈区的,栈区的大小<<堆区的大小。所以如果我们在栈区中开辟的vector中插入巨量的数据,是会导致栈溢出的。
解决办法是将数据存放到堆区上(使用new来创建vector,而不是直接创建)
2.如何实现多次运行程序但只有一个后台进程?
使用命名互斥锁,程序启动前申请锁。
- 如果锁没有被申请,代表是第一个进程,可以正常运行
- 如果锁已经被占用,代表已经有进程了,直接退出当前进程(这里要使用try_lock避免阻塞等待)
在Linux下可以用命名信号量来实现类似进程共享锁的操作。这部分可以去学习进程通信中信号量的部分。
咨询了发这篇面筋的大佬,说是用文件保存之前进程的PID,读取出来将之前的进程kill掉。
3.二分法的前提是什么?
- 数据有序
- 数据结构支持随机访问
4.互斥锁和自旋锁有什么区别
- 互斥锁是在内核态进行阻塞等待
- 自旋锁是在用户态不断循环沦陷检测锁的状态
如果使用场景是较长运行的共享资源,那么就使用互斥锁。避免自旋锁不断沦陷检测消耗大量CPU资源。
如果使用场景的共享资源访问速度快,那么可以使用自旋锁。避免互斥锁频繁进行用户、内核态的转换而造成消耗(这里指其他需要获取锁的进程得进入内核态阻塞等待)
5.TCP三次握手除了序列号还发了什么其他东西?
- SYN和ACK这些表记位(具体复习三次握手每个阶段的发送)
- 双方服务进程的端口号
- 起始序列号和对对方发送的SYN报文的应答序列号
- TCP校验和
- TCP窗口大小
后续建立连接后,就会根据双方的窗口大小和数据的序列号开始相互通信。
6.子类重写父类函数,子类中该函数声明为private,能否重写成功?
用下面这个毛坯房来进行测试,在默认情况下,我们子类的重写函数都和父类有相同的作用域声明符。
1 | class A |
直接运行,结果也符合预期,目前调用的是子类重写后的虚函数,所有函数都重写成功。
1 | B::foo1 |
1 | class B : public A |
我尝试了各种修改作用域的方式,包括将子类中foo3函数改成公有,foo1函数改成私有,都能正常完成重写。这里的作用域声明符只是会改变子类外是否能调用这个函数,和能否完成虚函数重写无关!
请注意,如果你将继承方式由public改成private,那么就无法在类外使用父类指针指向子类对象了
1 | ╰─ g++ test2.cpp -o test |
但这依旧不影响子类函数重写父类函数(我的依据是override关键字没有报错)
7.pthread_create能传入类成员函数的指针吗
可以,但是必须是静态成员函数。
如果是普通成员函数,那就需要用中间函数来处理,比如下面的示例代码。
1 |
|
我们可以用std::function
包装一个类的成员函数,并用std::bind
将对象的this指针绑定到第一个参数上。外层再套上一个用于执行该函数的void函数,就能传给pthread_create
。
std::function
的对象不能直接传给C语言的函数指针,即便参数对应。会报错。
8.函数内static变量的作用
在函数内定义一个static变量,该变量只会在进入这个函数的时候初始化一次。
1 | // 计算某一月的1号是一年的第几天(不考虑闰年) |
比如上面的函数,我们定义的day数组就只会在第一次进入这个函数的时候初始化。之后进入这个函数将不在初始化,就节省了初始化一个数组的消耗。出了这个函数后,该数组变量依旧存在。
请注意,这个static语句并不只是变量只初始化一次,实际上这一行语句在该函数中都只会进行一次
1 | bool checkFlag() |
比如上面的代码中,如果用正常思维来理解,你会觉得这个if语句每次都会判断为真而进入其中。但实际上flag的定义只会被定义一次,只要我们在if中将其改成了true,那么下一次进入该函数的时候,flag依旧会是true,static bool flag = false;
语句会被直接跳过,并不会再次执行赋值!
我之前理解的就是flag变量只会被创建一次,但static后的赋值依旧会执行。这个理解是错误的!