Cpp面经

Cpp面经

fetch150zy

C++ 面经

wps

1、TCP三次握手

TCP(Transmission Control Protocol)三次握手是一种网络通信协议中用于建立可靠的连接的机制。这个过程在TCP/IP模型中是至关重要的,确保了数据的可靠传输。三次握手的主要目的是在两个TCP端点之间同步序列号和确认号,并交换其他控制信息。这个过程可以分为三个步骤:

  1. SYN(同步):初始步骤由客户端发起。客户端发送一个SYN(同步序列编号)报文到服务器,这个报文包含一个客户端选择的初始序列号
  2. SYN-ACK(同步确认):服务器接收到SYN报文后,会发送一个SYN-ACK报文作为响应。这个报文包含服务器自己的一个初始序列号,和客户端的序列号加一(这是对客户端SYN报文的确认)
  3. ACK(确认):最后,客户端收到服务器的SYN-ACK报文后,会发送一个ACK报文。这个报文包含客户端的初始序列号加一,和服务器的序列号加一;此时双方都已经完成三次握手,TCP连接建立成功

整个三次握手过程的目的是确保双方都能够接收和发送报文,从而建立起一个稳定的通信连接

2、TCP粘包问题

TCP粘包问题指的是在数据传输过程中,由于TCP的特性和传输机制,多个数据包可能会被接收端组合成一个大的数据块,从而造成数据解析错误;这种情况下,接收端无法正确识别每个独立的数据包

发生原因

  1. 数据发送速度和接收速度不一致
  2. TCP缓冲区大小限制

解决方案

  1. 使用消息定界
  2. 使用固定长度限制
  3. 使用消息头部信息

3、TCP四次挥手

TCP四次挥手是用于终止一个TCP连接的过程,确保双方都完成了数据传输并断开连接

四个步骤

  1. 发送方发送FIN
  2. 接收方确认ACK
  3. 接收方发送FIN
  4. 发送方确认ACK

为什么需要多一次挥手?

因为在关闭连接时,可能还有未被接收或处理完的数据在网络中传输,当接收到FIN报文时,只能表示对应端口上已经没有新的数据要传输了,并不能保证之前已经传输的全部数据都被接收和处理完毕;所以在进行第三次挥手时允许发送端还能继续等待一段时间来确保所有数据都被接收和处理完毕,才能发送最后一次确认的ACK报文段,这样可以避免数据丢失或被意外截断

4、Leetcode 141环形链表

5、Leetcode 236二叉树的最近公共祖先

6、hashmap出现哈希冲突,怎么解决

  1. 链地址法
  2. 开放地址法
  3. 二次哈希法
  4. 更好的散列函数

7、百万数据,找出TOP10全部算法

  1. 排序:O(nlogn)
  2. 堆排:利用大根堆来维护当前TOP10元素O(nlogk)
  3. 桶排:取决于数据范围以及桶内排序算法

8、快排时间复杂度

快速排序的平均时间复杂度为O(nlogn),最坏情况下时间复杂度为O(n^2),在序列基本有序情况下不推荐快排

9、怎么评判算法的好坏

  1. 时间复杂度
  2. 空间复杂度
  3. 正确性
  4. 可读性和可维护性
  5. 拓展性和灵活性
  6. 实际应用情况
  7. 资源利用率

10、vector怎么扩容

  1. 当向std::vector添加新元素时,如果当前元素数量超过了当前容量,则需要扩容
  2. 扩容时,通常会分配更大的内存空间来存储更多的元素(2倍规则)
  3. 旧数据会被拷贝的新内存空间中
  4. 扩容完成后,原有的内存空间会被释放

11、介绍一下项目reactor模式

Reactor模式是一种基于事件驱动的设计模式,用于构建高性能、可拓展的网络应用程序,它主要由以下几个组件组成:

  1. 事件循环
  2. 多路复用器
  3. 事件处理器
  4. 请求/响应模型

12、Muduo网络库拷贝

在Muduo网络库中,内存拷贝(Memory Copy)是指将数据从一个内存区域复制到另一个内存区域的操作;在网络编程中,数据的读取和发送通常需要进行内存拷贝

13、深拷贝浅拷贝

两种对象复制方式:

  1. 浅拷贝:创建一个对象,将原来对象的值或者引用复制到新对象中,如果原对象包含引用类型的成员变量,那么新对象和原对象会共享这些引用类型的成员,修改其中一个对象的成员可能会影响另一个对象
  2. 深拷贝:创建一个对象,并递归的复制原对象及其所有引用类型的成员,确保新对象与原对象完全独立,即使修改其中一个对象的成员,也不会影响到另一个对象

14、消息队列的使用

ZeroMQ为例:

  1. 安装ZeroMQ库
  2. 创建一个发送者和一个接收者
  3. 在发送者和接收者之间建立连接,使用指定的传输协议和地址
  4. 发送者将消息发送到指定的队列
  5. 接收者从队列中接收消息并进行处理

微信搜索后台开发

1、C++虚函数用在哪些场景和功能

  1. 多态
  2. 基类指针或引用操作派生类对象
  3. 运行时动态绑定
  4. 实现接口和抽象类
  5. 动态创建和销毁对象

2、是什么时候的多态,运行还是编译

C++中虚函数实现了运行时多态,也称为动态多态;在编译时,通过使用关键字virtual来声明基类中的函数为虚函数,然后在派生类中进行重写(覆盖)该虚函数;当使用基类指针或引用调用这个虚函数时,根据实际对象的类型,在运行时会动态绑定并调用相应的派生类函数

3、虚函数和纯虚函数有什么区别

  1. 实现方式:虚函数在基类中有默认的实现,而派生类可以选择是否重写该函数;纯虚函数在基类中没有具体的实现,只是声明,派生类必须提供自己的实现
  2. 使用限制:由于纯虚函数没有具体的实现,因此无法创建对象;一个类如果含有纯虚函数,则称为抽象类;抽象类不能被实例化,只能作为其他派生类的基类使用
  3. 接口定义:纯虚函数常用于定义接口规范,通过继承抽象基类并重写纯虚函数来完成特定功能
  4. 子类继承:子类可以选择性的重写父类中的虚函数,也可以不重写;对于纯虚函数,子类必须提供自己的实现

4、内存管理中C++的new和malloc的区别

  1. 类型安全性:new是C++的运算符,可以根据类型自动计算所需的内存大小,并返回正确类型的指针,而malloc是C标准库函数,只返回一个void*指针,需要手动进行类型转换
  2. 构造函数调用:使用new分配内存时,会自动调用对象的构造函数进行初始化;而使用malloc只是简单地分配一块内存空间,并不会调用构造函数。这意味着使用new分配内存时,对象被创建并初始化;而使用malloc分配内存时,必须手动调用构造函数来初始化对象
  3. 内存大小计算:当使用new运算符来分配数组时,编译器会根据数组元素的数量和大小自动计算所需的总内存量;而对于malloc函数,则需要手动计算所需内存大小,并通过参数传递给它
  4. 错误处理机制:当new无法成功分配所需的内存时(内存不足),会抛出异常(std::bad_alloc);而malloc在无法成功分配内存时返回空指针(nullptr),需要手动检查并处理错误情况

5、new可以重载吗,可以改写new函数吗

C++中,可以重载new运算符,但是不建议改写全局的new函数;重载new运算符可以用于自定义内存分配策略、添加日志记录等功能;通过重载类的成员函数operator new和operator delete可以实现对单个类对象的内存管理

6、C++中的map和unordered_map的区别和使用场景

C++中的std::map和std::unordered_map都是关联容器,用于存储键值对;它们的区别在于底层实现方式和性能特点

std::map是基于红黑树实现的有序关联容器,它按照键的顺序进行排序,并提供了较快的搜索、插入和删除操作;因为是有序的,所以适合需要按照键值排序或范围查找操作较多的场景

std::unordered_map是基于哈希表实现的无序关联容器看,它使用哈希函数将键映射到桶(bucket)中,并提供了常数时间复杂度的搜索、插入和删除操作;由于是无序的,所以适合需要快速查找单个元素而不考虑顺序问题的场景

7、它们是线程安全的吗

在标准库中并没有提供线程安全的保证

8、gcc编译的过程

  1. 预处理:源代码经过预处理器进行处理
  2. 编译:经过预处理后,中间文件会被传递给编译器,编译器将中间文件翻译为汇编语言代码,这些汇编语言代码与特定硬件平台相关的低级指令
  3. 汇编:接下来,汇编器将汇编语言代码转换为目标机器码,并执行一个目标文件,目标文件包含了可执行程序的二进制表示以及符号表信息
  4. 链接:如果程序依赖于其他库或模块,则链接器将被调用,连接器复杂解析和连接目标文件之前的引用关系,并将它们组合成最终的可执行程序或共享库

9、C++11的特性

  1. 自动类型推导(auto)
  2. 声明和初始化分离
  3. range-based for loop
  4. 移动语义
  5. Lambda表达式
  6. 空指针常量
  7. 强类型枚举
  8. 并发编程支持

10、介绍一下有哪些智能指针

C++11引入了三种智能指针

  1. shared_ptr:是一种引用计数智能指针,允许多个对象共享同一个资源,他会跟踪资源的引用计数,在最后一个使用者释放资源时自动销毁
  2. unique_ptr:是一种独占智能指针,提供了对单个所有权的管理,它不能被拷贝,只能通过移动语义来转移所有权,并在其生命周期结束时自动释放资源
  3. weak_ptr:是一种弱引用智能指针,主要用于解决shared_ptr循环引用的问题,与shared_ptr不同的是,其并不增加资源的引用计数,可以通过lock()方法获取一个有效的

11、RAII实现数据库连接池怎么实现的

  1. 创建一个数据库连接类,该类负责封装单个数据库连接的操作
  2. 创建一个连接池类,用于管理多个数据库连接对象;连接池内部维护一个可用连接的队列,以及一个已使用连接的集合
  3. 在连接池的构造函数中,初始化一定数量的数据库连接,并将它们添加到可用连接队列中
  4. 实现从连接池获取可用连接的方法,该方法从可用连接队列中获取一个空闲的数据库连接,并将其移至已使用集合中
  5. 实现释放已使用连接并返回到可用状态的方法,该方法将指定的数据库连接移回到可用队列中,以便其他线程可以重新使用它
  6. 使用RAII技术,在获取数据库连接时创建一个辅助对象,该对象在其析构函数中自动释放所持有的数据库连接

12、有没有想过其他方法管理数据库连接

  1. 手动管理:需要开发者自行管理连接的声明周期,确保正确地释放资源
  2. 连接池线程池:使用线程池来管理数据库连接,在程序初始化时创建一定数量的连接对象,并将它们添加到线程池中;当需要使用数据库时,从线程池中获取一个可用的连接,执行操作后将其返回给线程池进行复用,这样可以避免频繁的创建和销毁连接对象
  3. 连接池连接超时机制:在连接池中为每个连接设置一个超时时间,在获取连接时检查是否存在空闲但长时间未被使用的连接,如果存在,则关闭并移除该连接,并重新创建新的可用连接
  4. 动态库调整连接数:根据实际负载情况动态调整数据库连接数,通过监控系统负载和数据库响应时间等指标,自动增加或减少数据库连接数以满足需求并避免过多或不足的资源占用

13、排序算法

冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、计数排序、基数排序、

14、哪些是稳定的,哪些是不稳定的

  1. 稳定

    冒泡、插入、归并

  2. 不稳定

    选择、快速、堆、计数、基数

15、算法题:实现一个不限类型的线程安全的LRU,不能使用STL

深信服

其他题目上面都有提到

进程间通信方式

  1. 管道
  2. 命名管道
  3. 信号量
  4. 消息队列
  5. 共享内存
  6. 套接字
  7. 信号
  8. RPC

中望

虚析构函数

C++的虚析构函数是一个在基类中声明为虚函数的析构函数,它用于实现多态性,确保在删除指向派生类对象的基类指针时,会调用正确的派生类析构函数

x64内存对齐

x64架构中的内存对齐是指在分配内存时,将数据按照一定规则对齐到特定字节边界上,以提高访问速度和效率。这种对齐可以减少内存读写的次数,并且在某些情况下能够优化CPU缓存的使用

x64内存对齐的原则和机制:

  • 原则:结构体或类变量的首地址必须是其成员中最宽基本类型变量的倍数
  • 机制:编译器会在结构体或类变量中插入填充字节,使得结构体或类变量的大小是其成员中最宽基本类型变量的倍数

智能指针及运用场景

多线程下的观察者模式,C++11的单例模式,工厂模式,这些设计模式的好处

观察者模式:

  • 解耦性:观察者模式可以将被观察对象与观察者对象解耦,使它们之间的交互变得松散,从而提高代码的灵活性和可维护性
  • 可拓展性:通过添加或删除观察者对象,可以方便地增加或修改系统中的功能
  • 多线程支持:在多线程环境下,观察者模式能够保证被观察对象和观察者对象之间的同步

C++11单例模式:

  • 线程安全性:使用C++11提供的特性(如std::call_once、std::atomic)实现单例模式可以确保在多线程环境下仅创建一个实例,并且保证线程安全性
  • 懒加载:C++11的单例模式允许实例在需要时进行懒加载,延迟初始化以节省资源开销
  • 全局访问点:单例模式提供了一个全局访问点,方便其他代码获取该实例

工厂模式:

  • 解耦性:工厂模式将对象创建过程封装在工厂类中,客户端代码与具体代码产品类解耦,提高了代码的可维护性和可拓展性
  • 拓展性:通过添加新的具体产品类和对应的工厂方法,可以方便地增加系统中的功能,而无需修改现有代码
  • 隐藏实现细节:工厂模式隐藏了对象创建的具体实现细节,使客户端只需要关注接口而不必关心对象如何创建

进程通信方式,优缺点

线程同步手段,如何选择用哪个

explicit

移动语义,完美转发源码实现

移动语义底层实现

百度软件开发

虚拟内存

讲讲指针

并发和并行

iOS软件开发和Android软件开发的区别

讲一讲http和https

讲一讲堆栈

七层网络参考模型

找出一个字符串中最长不重复子串的长度(不能暴力)

百度二面

char*和char[]的区别,分别存储在哪

sizeof和strlen的结果分别是什么

算法题:翻转字符串中单词的顺序

项目Reactor和Proactor的区别

数据库的存储引擎和索引存储类型

B+树和哈希索引的区别

哈希冲突怎么解决

写状态机的伪代码

写ip地址转32位整型

快手

手撕题:k个链表合并

C++中多继承会导致类继承了多个基类,可能存在同名虚函数的情况,出现二义性,如何处理二义性

TCP三次握手过程、是否熟悉TLS协议

C/C++区别、堆栈的问题

内存泄漏处理经验

上海期货信息技术

C++多态是什么

虚函数是什么?纯虚函数是什么?

类模板的特化是什么

Tcmalloc用过吗?它的底层是什么样的?

define和inline有什么区别

重载是什么?底层是怎么实现的?

vector和deque有什么区别?是线程安全的吗?如何实现的呢?

vector是如何扩容的?

无锁结构如何实现多线程?

gdb如何查看函数的堆栈情况

硬连接和软连接有什么区别

UDP如何实现可靠传输?为什么不直接使用TCP?

Valgrind用了哪些组件?怎么从结果分析问题?

如何让一个类在堆上生成?

C++11了解过多少?

unique_ptr如何实现

移动语义是什么?举例说明

说几个常用的设计模式?设计模式的优点和缺点

为什么有人说单例模式不应该算作一种设计模式?

情景题:有两个在内存中的系统,一个是学生信息管理另一个是教师打分系统;更新这两个系统的话,如果使用自己写的内存分配器,应该如何设计

腾讯

算法题:一个很长的数字字符除一个小于10的整数,保留两位小数

进程线程和协程的区别

进程切换为什么比线程切换效率低

什么是一致性哈希

了不了解分布式一致性

TCP三次握手的过程以及状态转换

如果TCP没有滑动窗口机制还能正常工作吗

Socket通信服务端和客户端的流程

滑动窗口能进行拥塞控制吗

如何确定一个流程图有没有互相引用

10亿个数字取前100大怎么取

大根堆具体的原理是什么

Redis基本数据结构有哪些

此页目录
Cpp面经