面经1
1.单例模式的应用场景?
- 日志记录器:整个应用程序只需要一个日志记录,可以防止被重复创建。
- 文件配置初始化类
2.HTTP协议常见状态码
1xx (信息):临时响应 - 已收到请求,正在继续处理。
2xx (成功):服务器已成功接收并接受请求。
3xx (重定向):需要采取进一步操作才能完成请求。
4xx (客户端错误):请求包含错误,无法实现。
5xx (服务器错误):服务器无法完成请求。
- 200(成功) - 服务器成功返回网页
- 201(已创建) - 请求成功并且服务器创建了新的资源。
- 304(未修改) - 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
- 400(错误请求) - 服务器不理解请求的语法。
- 404(未找到) - 请求的网页不存在
- 500(服务器内部错误) - 服务器遇到错误,无法完成请求。
- 503(服务不可用)- 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
3.前向声明的作用?
前向声明主要用于.h文件中,仅告诉编译器某个类的存在,而没有提供类的具体实现。
作用:
减少编译依赖,加快编译速度:当一个头文件中包含另一个类的完整定义时,任何对该头文件的修改都会导致所有依赖该头文件的源文件重新编译。
解决循环依赖问题:不使用前向声明,两个类的头文件中可能出现相互引用。
1
2
3
4
5
6
7
8
9
10
11// A.h
class A {
B* b;
};
// B.h
class B {
A* a;
};
4.TCP/IP五层模型各自常用的物理设备?
- 应用层: 应用层直接面向用户的应用程序,为用户提供各种网络服务。不涉及特定的物理设备。
- 传输层: 提供端到端的通信服务。不涉及特定的物理设备。
网络层: 网络层负责路由选择及数据包的转发,确保数据可以从源端到达目的端。
- 路由器:根据IP地址进行数据包的转发,从而实现在不同网络之间的通信。
数据链路层: 处理节点间的可靠数据传输,包括错误检测与纠正。
- 交换机:通过MAC地址来转发数据帧
- 网桥: 用于连接两个或更多的局域网段。
物理层: 负责在物理介质上传输原始比特流。
- 网线、光纤、中继器、集线器
5.进程间通讯方式,各自的优缺点?
管道
- 简单易用,但对于非亲缘关系的进程不适用,且性能受限于I/O操作。
消息队列: 为进程提供了异步通信的方式,消息可以被发送到队列中,并由另一个进程按顺序读取。
- 适用于那些需要保证消息传递顺序及可靠性的应用场景。
共享内存: 允许多个进程访问同一块物理内存空间,从而实现高效的数据交换。
- 优点:速度快,适合需要频繁或大量数据传输的情况
- 缺点:需要处理同步问题,避免竞态条件。
套接字:支持本地和网络上的进程间通信。
信号: 用于通知进程发生了特定事件,比如异常终止等,本身不是用来传输复杂数据的。
6.介绍一下匿名管道和命名管道
匿名管道:
- 用于具有亲缘关系的进程之间通信,如父子进程。
- 仅存在于内存中,并没有实体文件与之对应。
- 匿名管道是单向的,意味着数据只能在一个方向上流动。
命名管道(FIFO):
使用一个特殊路径来标识,任何知道该路径的进程都可以打开并使用这个管道进行通信,不论这些进程是否具有亲缘关系。
保证了数据按照先进先出的原则被读取。
命名管道是一个实际存在的对象,可以在文件系统中看到它。
- 相较于匿名管道,配置和管理更加复杂。
7.哪些函数不能定义为虚函数
- 构造函数
- 静态成员函数
- 友元函数
- inline内联函数:inline内联函数是在编译时被展开,虚函数实在运行时才能动态绑定函数。
8.为什么模板类通常放在头文件中?
模板实例化是指编译器基于模板定义和提供的具体类型参数生成具体的类或函数版本的过程。
普通类的代码在编译阶段生成,模板类的代码在实例化(编译阶段)的阶段生成。
模板实例化时,编译器需要看到模板的定义,以便生成相应的代码。所以模板类一般在头文件中要有具体的定义。
分离编译的条件:
模板实现放在.cpp文件中,同时在该文件中给出所有可能的模板实例化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// MyTemplate.cpp
// 模板类的实现
template <typename T>
MyTemplate<T>::MyTemplate() : value(T()) {}
template <typename T>
void MyTemplate<T>::print() {
std::cout << "Value: " << value << std::endl;
}
// 显式实例化
template class MyTemplate<int>; // 实例化 MyTemplate<int>
template class MyTemplate<double>; // 实例化 MyTemplate<double>
9.静态库和动态库的区别
静态库:
扩展名:
- 在Unix/Linux系统上,以
.a
为扩展名 - 在Windows系统上,以
.lib
为扩展名。
- 在Unix/Linux系统上,以
链接时机:在编译阶段,静态库中的代码会被完整地复制到最终生成的可执行文件中
优点:
- 编译后的可执行文件包含了所有需要的库代码,因此无需担心依赖问题。
缺点:
- 由于每个使用该静态库的应用程序都会包含一份库代码,这会导致生成的可执行文件体积增大。
- 如果静态库更新了,所有依赖它的应用程序都需要重新编译链接。
动态库:
扩展名:
- 在Unix/Linux系统上,以
.so
作为扩展名。 - 在Windows系统上,以
.dll
作为扩展名。
- 在Unix/Linux系统上,以
链接时机:动态库只在程序启动时或者运行过程中按需加载。
优点:
- 多个程序可以共享同一个动态库实例,节省内存和磁盘空间。
- 方便更新:库文件更新后,只要接口不变,已有的应用程序无需重新编译即可使用新版本的库。
缺点:
- 程序运行时需要确保相应的动态库存在,并且版本兼容,否则可能导致程序无法启动或出现错误。
- 相对于静态库,可能会有稍微复杂的依赖管理和部署要求。
10.线程池有什么缺点
线程池中的线程数量配置:
- 如果任务量超过了线程池的最大容量,额外的任务可能需要等待空闲线程,这可能会导致响应时间增加。
- 设置的线程数过多,可能会导致系统资源(如内存、CPU)过度消耗,影响整个系统的性能
11.RAII
使用局部对象来管理对象资源。局部对象是存储在栈上的对象,生命周期由操作系统来管理,不需要人工介入。对象获取即初始化,当不再需要使用即自动释放资源
12.线程安全
数据竞争: 互斥锁
原子操作: 原子操作是不可分割的操作,不会被其他线程中断。
死锁:
按顺序加锁:确保所有线程以相同的顺序获取锁。
使用超时机制:尝试加锁时设置超时时间。