【面经】C++面筋记录
记录部分从牛客网上看到的面筋
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后的赋值依旧会执行。这个理解是错误的!
9.私有static成员函数的意义?
这个问题比较有意思,我们知道static函数属于整个类,可以直接通过类名作用域调用。且static函数中无法访问任何非static的成员变量。
但是,如果给你个static的私有成员函数,它又有什么意义呢?
- 私有static成员是无法通过类名调用的
- 他也没有办法访问类中非static成员变量
可以这么理解:我有一个方法只在这个类里面需要,这个方法不需要使用成员变量,可以通过传参实现(比如计算什么的)
但是,我又不想它的命名污染父作用域。
那么,我就可以把它写为类的私有成员函数,并加上static告诉其他人,这个函数是一个单纯的方法类,不需要使用类的成员变量。
当然,加上static只是一个编程习惯罢了,实际上这种情况不写static也无所谓。
10.写出数组指针的定义,指针数组的定义,函数指针的定义,指针函数的定义,函数指针数组的定义
1 | 数组指针:int (*p)[10]; (一个指向存放10个int元素数组的指针) |
11.面向对象和面向过程的区别?
C++是面向对象的语言,C是面向过程的语言。
基本理念:
- 面向对象OOP:基于对象的概念,将数据和操作方法封装在一起,以创建对象。对象可以被看做具有特定功能的独立个体,他们之间可以通过消息传递进行通信。
- 面向过程:将问题划分为一系列步骤,定义函数来实现每个步骤。代码以函数组成,函数依照顺序调用来完成任务。
这里还需要记住面向对象的三大特性:封装、基础、多态。
- 封装:封装类内部成员和函数实现,只提供接口供外部调用;
- 继承:允许子类在继承的基础上扩展,代码编写更容易;
- 多态:用父类调用子类的函数,即不同对象对同一个功能做出不同的响应,提高灵活性和扩展性。
面向过程是顺序性(函数定义顺序执行任务)和功能性(主要关注功能实现,强调函数和过程的设计以及调用)