腾讯实习
指针常量和常量指针
常量指针(const Type* ptr):指针指向的内容不能被改变,但指针本身可以改变指向。
指针常量(Type* const ptr):指针自身的值即内存地址不能改变,但指向的内存内容可以被改变。
int x/8性能优化
使用位运算进行性能优化,可以将 int x/8 替换为 x >> 3,这样利用位移操作来代替除法运算。
介绍虚函数及其用法
虚函数是一个在基类中声明为virtual的函数,它可以在派生类中被重写,以实现多态。当通过基类的指针或引用调用一个虚函数时,会根据对象的实际类型来调用对应的函数实现,这个过程称为动态绑定。
为什么构造函数不能是虚函数
构造函数不能被声明为虚函数,因为虚函数依赖于对象的虚函数表来实现动态绑定,但是当构造函数被调用时,对象正在被构建,虚函数表尚未建立,因此无法调用。另外,构造函数的任务是初始化对象的状态,而多态是针对已经初始化的对象进行操作的概念,因此将构造函数设为虚函数也没有意义。
虚函数表占几个字节
虚函数表的大小取决于编译器的实现和类的设计。在一般情况下,一个类如果包含虚函数或者继承了有虚函数的父类,编译器会为该类的每个对象添加一个虚函数表指针。在大多数32位系统上,一个指针的大小是4字节;在64位系统上,一个指针的大小是8字节。
虚函数表本身是一个指针数组,其大小等于虚函数数量乘以指针的大小。
指针和引用的区别
-
存储和使用:指针是一个变量,存储的是一个地址,可以被重新赋值和修改;引用是一个已存在变量的别名,初始化后就不能重新绑定。
-
空值:指针可以是nullptr,引用必须绑定到一个合法的对象。
-
操作符:指针需要解引用操作符 * 来访问目标对象,引用直接访问。
-
地址获取:可以使用 & 来获取指针指向的内存地址,对于引用,&得到的是引用所绑定对象的地址。
-
指针可以有多级(如双指针、三指针等),但是没有“多级引用”。
-
C++中构造和析构函数的形参通常使用引用,以避免对象的复制。特别是对拷贝构造函数和赋值操作符,其形参必须是引用。而普通的函数则可以根据需要选择使用指针或引用。
c++面向对象三大特征
封装、继承、多态
vector和quene区别
vector是一个动态数组,支持随机访问,可以在任何位置插入和删除元素,但效率较低(尤其是在头部)。
queue是一个先入先出(FIFO)的数据结构,只能在队尾插入,在队首删除。
vector对于随机访问有很高的效率,但在头部或中间插入和删除元素的效率较低。
queue在其两端操作(入队和出队)具有较高的效率,但不支持随机访问。
https和http区别
- 安全性:HTTPS提供了加密传输和身份验证,保障数据在传输过程中的安全性,而HTTP传输的数据是未加密的,容易被第三方窃取或篡改。
- 端口:HTTPS默认使用443端口,HTTP默认使用80端口。
- 性能:由于HTTPS在传输数据前需要进行加密,可能会略微影响传输效率。然而,随着技术发展,这种影响正在减小。
https如何实现加密/解密
HTTPS通过SSL/TLS协议实现数据的加密和解密。具体运作过程如下:
- 密钥交换:客户端和服务器之间通过安全握手协议(Secure Sockets Layer Handshake Protocol)交换加密算法和会话密钥。
- 数据加密:通过交换的会话密钥,服务器和客户端将通信内容进行加密,确保数据的隐私性。
- 数据解密:收到加密数据的一方使用会话密钥对数据进行解密,获取原始信息。
- 数据完整性:通过消息完整性检查,验证数据是否在传输过程中被篡改。
数字证书什么作用
- 身份验证:证明服务器的身份,避免用户连接到伪装的站点。
- 加密传输:提供公钥,以供客户端和服务器之间建立安全的加密通信。
- 数据完整性:确保传输的数据没有被篡改。
如果没有被授权的证书如何通过验证
- 自签名证书:用户可以创建自己的证书,并手动将它添加到信任的证书列表中。但是,这种方式的显著缺点是,在不同设备或用户之间同步信任列表可能会很困难。
- 忽略证书错误:在一些应用中,可以设置成忽略掉SSL/TLS的证书错误。但这样做将严重降低了网络安全性,使得中间人攻击成为可能,因此在实际操作中要小心使用。
如何解决线程竞争
解决线程竞争可以通过以下方法:
- 互斥锁(Mutex):确保同一时间内只有一个线程可以访问共享资源。
- 读写锁(Read-Write Lock):允许多个线程读共享资源,但写操作是互斥的。
- 原子操作:使用原子操作直接对数据进行操作,确保操作的原子性。
- 信号量(Semaphore):通过计数信号量控制同时访问资源的线程数。
- 条件变量(Condition Variable):允许线程在特定条件下等待或发送通知,以避免竞争。
锁分类有几种方式
按照功能分类:
- 互斥锁(Mutex)
- 读写锁(Read/Write Locks)
- 递归锁(Recursive Locks)
按照实现机制分类:
- 自旋锁(Spinlock)
- 阻塞锁(Blocking Lock)
- 乐观锁/悲观锁
按照作用范围分类:
- 全局锁
- 细粒度锁
特殊类型的锁:
- 条件变量(Condition Variables)
- 信号量(Semaphore)
- 读写分离锁(Read-Write Lock)
c++不声明c语言标准库会怎么样
如果在C++代码中不声明或引入C语言的标准库,那么在代码中就无法使用该库提供的函数和变量。
为什么要声明
在 C++ 中声明 C 语言的标准库是为了在跨平台编程过程中复用 C 语言的功能和特性。
怎么查找函数位置有什么区别
查找函数的位置可以通过以下方式:
- 阅读文档:最直接的查找函数位置的方法是查看其官方文档或参考手册。
- 使用IDE:集成开发环境(IDE)通常有内置的搜索功能,可快速找到函数定义。
- 使用grep:在Linux环境中,可以使用grep工具在文件中搜索文本模式。
- 使用代码索引工具:如ctags或Source Insight,这些工具可以在大型项目中快速定位函数定义。
多线程编程过程
多线程编程过程通常包括以下步骤:
- 创建线程:确定需要多线程执行的任务,并使用相应的API创建线程。
- 定义线程任务:编写执行线程功能的代码块,定义线程开始执行时要调用的函数或方法。
- 启动线程:初始化线程并开始执行定义的任务。
- 线程同步:使用互斥锁、信号量等机制同步多个线程间的操作,避免数据冲突。
- 管理线程生命周期:监控线程的执行状态,实现线程的暂停、继续和停止等控制。
- 线程通信:通过共享内存、消息队列等方式实现线程间的数据交换和通信。
- 回收资源:线程任务完成后,回收线程所占用的资源。
tcp三次握手
TCP 的三次握手指的是建立一个 TCP 连接时需要经过的三个步骤:
- SYN:客户端发送一个 SYN(同步)包给服务器,请求建立连接,同时发送自己的初始序列号。
- SYN-ACK:服务器收到 SYN 包后,向客户端发送一个 SYN-ACK(同步应答)包,确认收到了 SYN 包,同时也发送自己的初始序列号。
- ACK:客户端收到 SYN-ACK 包后,再发送一个 ACK(应答)包给服务器,确认收到了服务器的 SYN-ACK 包。至此,连接建立完成。
海康威视应用软件开发工程师春招
指针和引用的区别
-
存储和使用:指针是一个变量,存储的是一个地址,可以被重新赋值和修改;引用是一个已存在变量的别名,初始化后就不能重新绑定。
-
空值:指针可以是nullptr,引用必须绑定到一个合法的对象。
-
操作符:指针需要解引用操作符 * 来访问目标对象,引用直接访问。
-
地址获取:可以使用 & 来获取指针指向的内存地址,对于引用,&得到的是引用所绑定对象的地址。
-
指针可以有多级(如双指针、三指针等),但是没有“多级引用”。
-
C++中构造和析构函数的形参通常使用引用,以避免对象的复制。特别是对拷贝构造函数和赋值操作符,其形参必须是引用。而普通的函数则可以根据需要选择使用指针或引用。
TCP三握四挥的过程
TCP 的“三次握手”和“四次挥手”过程如下:
三次握手(建立连接):
- SYN: 客户端发送 SYN 标记的数据包到服务器以初始化一个连接。
- SYN-ACK: 服务器接收到 SYN 包,回复一个 SYN-ACK 标记的数据包作为应答。
- ACK: 客户端再发送一个 ACK 包,确认收到服务器的 SYN-ACK 包,完成连接建立。
四次挥手(断开连接):
- FIN: 主动关闭方发送一个 FIN 标记的数据包表示要关闭连接。
- ACK: 被动关闭方接收到 FIN 包后,发送一个 ACK 包作为应答,并进入 CLOSE_WAIT 状态。
- FIN: 被动关闭方准备好关闭连接时,发送一个 FIN 包。
- ACK: 主动关闭方接收到 FIN 后,回送一个 ACK 包确认,然后进入 TIME_WAIT 状态。经过一段时间后,确认没有新的数据要处理,连接完全关闭。
GET和POST的区别
数据传输方式:
- GET: 参数通常附加在 URL 后面,有长度限制,可见。
- POST: 参数在请求体中,无长度限制,不可见。
安全性:
- GET: 安全性较低,因为数据在 URL 中可见。
- POST: 安全性较高,因为数据在请求体中。
可缓存/收藏:
- GET: 可被缓存,也可被收藏为书签。
- POST: 通常不被缓存,也不能收藏为书签。
功能用途:
- GET: 适用于请求数据或搜索查询,URL中保存状态信息。
- POST: 适用于提交数据,如表单提交。
幂等性:
- GET: 幂等,即多次请求相同结果。
- POST: 非幂等,多次请求可能产生不同的副作用。
双向链表,环形链表
- 双向链表:每个节点有两个链接,一个指向前一个节点,另一个指向后一个节点。这使得从任何方向- 遍历链表变得简单。
- 环形链表:在这种链表中,最后一个节点指向第一个节点或头节点,形成一个闭环。这使得链表从任何节点开始都可以遍历完整个链表。
C++内存管理
C++的内存管理主要有以下几部分组成:
- 堆(Heap):程序员手动控制的内存区域,使用 new 分配内存,使用 delete 释放内存。
- 栈(Stack):存放局部变量和函数参数的内存区域。变量的分配和销毁由编译器自动进行,生命周期随函数的调用和返回而改变。
- 静态/全局区(Static/Global):存放全局变量和静态变量,由编译器分配和销毁,并在程序运行期间始终存在。
- 常量区: 存放如字符串常量等的内容,只可读。
- 代码区:存放程序代码(函数体),由操作系统进行管理。
内存池
C++内存池的基本工作原理和步骤是:
- 首先预先在内存中申请足够多的内存块,这些内存块形成内存池。
- 当有新的内存请求时,从内存池中分配出一个足够的内存块来使用,而不是从系统堆上分配。
- 当不需要使用的内存返回时,将其放回内存池中,以便以后使用,而不是返回给系统。
高德 C++
const用在哪?
定义常量、修饰函数参数、修饰函数、修饰指针
const & 传参为什么效率高?
使用const &(常量引用)传参效率高主要是因为它避免了大型对象的拷贝操作,减少了内存使用和提高了函数调用的速度。
C++容器?
序列式容器:元素按顺序排列。
- vector:动态数组,支持快速随机访问。
- list:双向链表,支持快速插入和删除。
- deque:双端队列,支持从两端快速插入和删除。
- array(C++11):固定大小数组。
- forward_list(C++11):单向链表。
关联式容器:基于键来存取元素。
- set:不重复元素集,自动排序。
- multiset:可以有重复元素的集,自动排序。
- map:键值对集合,根据键自动排序。
- multimap:键值对集合,键可以重复,根据键自动排序。
vector超出容量会怎样?
当向C++的vector添加元素超出其当前容量时,它会自动进行扩容。通常,这意味着vector将分配一个新的、更大的内存块,通常是当前容量的两倍,并将现有元素复制到新的内存块中,然后释放旧的内存。
vector扩容基数?
vector扩容的具体基数(或增长因子)依赖于具体的实现,并没有在C++标准中严格规定。常见的扩容基数是当前容量的1.5倍或2倍。扩容后的新容量被称为vector的capacity。
虚函数表是类还是对象拥有的?
虚函数表是类的一部分,但是每个对象实例都有一个指针指向其所属类的虚函数表。所以虽然虚函数表是类拥有,但通过对象也能访问到。
怎么获取虚函数表?
可以通过对象的虚表指针来获取虚函数表。这通常涉及到对对象内部布局的直接访问,不推荐在标准C++代码中这样做,因为这种操作是依赖于具体编译器实现的,不具有可移植性。
map查询效率?
unordered_map通常提供平均时间复杂度为O(1)的查找性能,而map提供O(log n)的查找性能,因为它是基于红黑树实现的。
map的key是自定义的类,需要注意什么?
使用自定义类作为map的键时,需要为类实现比较运算符<的重载,才能保证类的对象之间可以进行完全排序比较,这是map红黑树底层实现的要求。如果没有这个操作,编译器会报错。
二叉树遍历?
二叉树的主要遍历方式有三种:
前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树。
后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。
查找算法?
查找算法主要包括线性查找、二分查找、哈希查找以及基于各种树结构的查找
收集整理了一份2024年最新C++开发学习资料,既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C++开发知识点,真正体系化!
包含大厂面经、学习笔记、实战项目、大纲路线、讲解视频 领取 君羊739729163 或者
https://docs.qq.com/doc/DR2N4d25LRG1leU9Q
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。