火烈鸟网络C++开发
空类对象的大小?
C++中空类的对象大小为1字节
——————————————————————————————————
哪些函数不能为虚函数?
构造函数、静态成员函数、内联函数、友元函数
——————————————————————————————————
含有虚函数的空类对象大小?
含有虚函数的空类对象大小通常为一个指针的大小,在32位系统上通常是4字节,在64位系统上通常是8字节。
——————————————————————————————————
模板类可以不可以继承?
可以继承
——————————————————————————————————
new和malloc的区别?
new
在分配内存时自动计算所需大小,并返回正确的类型指针,无需进行类型转换;而 malloc
需要手动计算字节数,并返回 void*
类型,使用时通常需要强制类型转换。
new
会调用对象的构造函数初始化对象,并且与之对应的 delete
会调用析构函数;malloc
只负责分配内存,不调用构造函数,与之对应的 free
也不调用析构函数。
当内存分配失败时,new
会抛出 bad_alloc
异常(除非使用了 nothrow
版本),而 malloc
会返回 nullptr
。
new
/delete
是C++中的操作符,malloc
/free
是C语言中的库函数。
——————————————————————————————————
模板能否偏特化?
可以偏特化
——————————————————————————————————
vector如何工作的?底层介绍下
- 内存分配:当你创建一个
vector
时,它会预分配一定量的内存。这个量通常是其容量(capacity),即在需要重新分配之前vector
可以保存的元素数量。 - 自动扩容:当新元素被添加到
vector
并且当前容量不足以容纳它时,vector
会自动扩大其内存空间。通常,这是通过分配一个更大的内存块,复制现有元素,然后释放旧内存来实现的。 - 元素访问:
vector
提供了像operator[]
这样的访问操作符来进行快速索引访问,因为其数据是连续存储的。 - 元素添加:元素可以通过
push_back
和emplace_back
方法添加到vector
的末尾。如果有必要,这些操作还会触发自动扩容。 - 内存管理:
vector
会自动管理其内存的生命周期。当对象被销毁时,分配的内存会被释放。使用reserve
方法可以显式增加容量,而shrink_to_fit
方法可以请求减少未使用的容量。
——————————————————————————————————
queue队列怎么清空元素?
要清空 std::queue
容器中的所有元素,可以使用 while 循环调用 pop()
方法,直到队列为空。
——————————————————————————————————
C++11新特性有哪些异步操作?
-
std::async
:一种启动异步任务并返回std::future
,用于稍后获取任务的结果的机制。 -
std::future
:表示异步操作结果的类,提供了获取计算结果的方法。 -
std::promise
:一种可以从线程中设置值的机制,其值可以通过关联的std::future
对象来获取。 -
std::packaged_task
:将一个函数或可调用对象包装为一个异步操作,这个操作的结果或异常存储在std::future
对象中。
——————————————————————————————————
智能指针怎么做到内存管理的?
智能指针通过封装原始指针,并自动管理其生命周期来实现内存管理。当智能指针的实例被销毁(例如,超出作用域或被显式删除)时,它会自动释放其管理的原始指针所指向的内存。
——————————————————————————————————
一个业务,客户端的异步请求,怎么变为同步操作?
将一个异步请求转换为同步操作可以通过使用std::future
和std::async
来实现。
- 使用
std::async
启动异步任务:std::async
启动一个异步任务,并返回一个std::future
对象,该对象在未来某个时点可以用来获取异步任务的结果。 - 通过
std::future::get
方法等待异步任务完成并获取结果:std::future::get
会阻塞调用线程,直到异步任务完成,并返回任务的结果。此时,原本的异步操作就转换为了一个同步操作,因为调用者必须等待异步任务完成才能继续执行。
——————————————————————————————————
单链表怎么找到中间节点?
使用快慢指针算法
- 初始化两个指针,
slow
和fast
,都指向链表的头节点。 - 移动这两个指针,
slow
每次向前移动一个节点,fast
每次向前移动两个节点。 - 当
fast
到达链表末尾或者fast
的下一个节点为空时(这取决于链表的长度是奇数还是偶数),停止移动。此时slow
指针指向的就是链表的中间节点。
——————————————————————————————————
设计一个定时器链表每次都要遍历怎么优化?追问我为什么?
不需要遍历整个链表,只需要遍历到前面的超时部分。因为链表中这个点之后的所有定时器都设置为在未来超时,因此它们现在不需要被处理。这样可以减少不必要的遍历,提高效率。
——————————————————————————————————
IO多路复用除了网络还有哪些用途?
- 监控多个文件描述符的事件,包括管道和串口。
- 同时管理用户输入和文件读写操作。
- 实现基于事件驱动的服务器,如数据库和Web服务器。
- 在进程间通信中同步或传输数据。
- 监听操作系统内核发出的各类信号。
——————————————————————————————————
如何解决频繁分配对象导致的内存碎片问题?
- 使用内存池:预分配一大块内存,然后从这个内存池中分配小片内存给对象使用,可以减少系统的内存分配次数,从而减少内存碎片。
- 对象缓存:重用已经分配但不再使用的对象,而不是频繁创建和销毁。
- 定制分配器:为特定的对象创建定制的内存分配器,优化内存分配和回收策略,减少碎片产生。
- **块分配:对于需要频繁创建和销毁的小对象,采用块分配策略,即一次性分配一大块内存,然后在这上面分配小对象。
- 智能指针:虽然它们主要用于管理对象生命周期和避免内存泄漏,但配合对象缓存等技术使用,也可以减少频繁分配和销毁对象,间接减少内存碎片问题。
——————————————————————————————————
tcp发送数据包内核经过了几次拷贝?
在TCP发送数据时,一般情况下,内核会进行两次拷贝操作:第一次从用户空间拷贝到内核空间的缓冲区,第二次从内核空间的缓冲区拷贝到网络接口的发送缓冲区。这就是所谓的"双拷贝"。
——————————————————————————————————
udp如何做到可靠传输?
UDP本身不提供可靠传输,但可以通过在应用层实现以下机制来增加其可靠性:
- 确认和重传:发送方维护未被确认的数据包列表,接收方收到数据后发送确认(ACK)。未收到ACK的包将被重发。
- 序列号:每个数据包都分配一个序列号,以便接收方检测丢失或重复的包。
- 校验和:数据包包含校验和,以检测在传输过程中的数据损坏。
- 超时重传:发送方设置超时计时器,如果在指定时间内未收到确认,重新发送数据包。
- 流量控制:避免发送方过快发送数据包,导致接收方处理不过来。
- 拥塞控制:根据网络状况调整数据发送速率,避免过多数据包导致网络拥塞。