文章目录
- C++面试大全
- 1. new/delete和malloc/free的区别
- 2. 多态是什么?如何实现?
- 3. 堆和栈的区别是什么?二者的数据结构是什么?那个效率更高?
- 4. 强制类型转换的使用
- 5. TCP的三次握手和四次挥手
- 6. 进程和线程的区别?
- 7. 5种IO模型
- 8. Linux中,显示第二行、最后一行,查找以.c结尾的文件怎么找?
- 9. git都要用那些命令?回滚是哪个命令?
- 10. 分组加条件查询的sql语句怎么写?
- 11. redis的常用数据类型?顺便说一下具体场景。
- 12. 黑盒测试和白盒测试详解
- 13. Linux基本命令
- 14. 数据库基本命令
- 15. redis的分布式锁?
- 16. mysql的索引?
- 17. 最左前缀匹配原则是什么?具体的数据结构是什么?
- 18. 讲一下事务和隔离级别以及对应的问题和解决?
- 19. 讲一下MVCC?
- 20. mysql里面有哪些锁?
- 21. 某个sql速度很慢,如何优化?
- 22. 说一下消息队列?
- 23. 指针常量和常量指针
- 24. 数据库中delete和trancate区别
- 25. C++是如何解决内存泄漏的?
- 26. 了解哪些排序方法?简述下快速排序。
- 27. 数据库三大范式。
- 28. 虚函数和纯虚函数的区别?
- 29. 重写和重载的区别?
- 30. 源文件到可执行文件的过程。
- 31. 深拷贝和浅拷贝的区别。
- 32. 进程间通信方式。
- 33.
- 34. 栈和队列的区别?
- 35. 了解哈希吗?如何解决哈希冲突?
- 36. 多线程,包括多线程编程,线程通信,线程互斥?
- 37. 什么是野指针?
- 38. 什么情况会出现野指针?
- 39. 死锁
- 40. 僵尸进程
- 41. 缓存池怎么实现的
- 42. 数组和链表的区别?优缺点?
- 43. IO多路复用
- 44. mysql的索引?B+树是什么?
- 45. SQL优化,查询量过大、查询过慢如何优化?
- 46. 覆盖索引和联合索引是什么?
- 47. 七层模型和四层模型
- 48. 为什么最后一次挥手要等待?
C++面试大全
1. new/delete和malloc/free的区别
malloc需要给定申请内存的大小,返回的指针需要强转。
new会调用构造函数,不用指定内存大小,返回的指针不用强转。
2. 多态是什么?如何实现?
多态(Polymorphism)是面向对象编程的一个重要概念,它允许不同类型的对象对同一个消息做出不同的响应。多态性使得程序能够根据对象的实际类型来执行相应的操作,提高了代码的灵活性和可扩展性。
实现多态性的方式有两种:静态多态(编译时多态)和动态多态(运行时多态)。
静态多态(编译时多态):
函数重载(Function Overloading):在同一个类中定义多个同名函数,但参数列表不同。编译器根据调用时传入的参数类型和数量来决定调用哪个函数。
运算符重载(Operator Overloading):重载内置运算符,使其能够对自定义类型进行操作。 动态多态(运行时多态):虚函数(VirtualFunctions):通过在基类中声明虚函数,并在派生类中进行重写,实现运行时的多态性。通过基类指针或引用调用虚函数时,会根据实际对象的类型来调用相应的函数。
抽象类和纯虚函数(Abstract Classes and Pure VirtualFunctions):抽象类是包含纯虚函数的类,纯虚函数是在基类中声明但没有具体实现的虚函数。抽象类不能被实例化,但可以作为基类用于派生其他类。
在实现多态性时,需要注意以下几点:使用基类指针或引用来操作派生类对象。 基类中的虚函数需要使用virtual关键字进行声明。
派生类中的重写函数需要使用override关键字进行声明(C++11及以上版本)。
多态性可以提高代码的灵活性和可维护性,使得程序能够更好地适应变化和扩展。
3. 堆和栈的区别是什么?二者的数据结构是什么?那个效率更高?
数据结构:
堆:堆是一种动态分配的内存区域,用于存储动态分配的对象。堆使用堆数据结构,通常是一个完全二叉树的结构,可以通过指针进行访问和操作。
栈:栈是一种后进先出(LIFO)的数据结构,用于存储局部变量、函数调用和程序执行的上下文信息。栈使用栈数据结构,通常是一个线性结构,通过栈顶指针进行访问和操作。
特点和用途:
堆:堆是动态分配的内存区域,大小不固定,可以在程序运行时进行动态分配和释放。堆通常用于存储动态创建的对象、大型数据结构和全局变量等。堆的生命周期由程序员手动控制,需要手动释放分配的内存,否则可能导致内存泄漏。
栈:栈是静态分配的内存区域,大小固定,由编译器自动分配和释放。栈通常用于存储局部变量、函数调用和程序执行的上下文信息。栈的生命周期由程序的执行流程自动管理,不需要手动释放内存。
效率:
堆:堆的动态分配和释放需要较多的时间和空间开销,因为需要进行内存管理和碎片整理。堆的分配和释放是非常灵活的,但可能会导致内存碎片化。
栈:栈的分配和释放是由编译器自动管理的,速度较快。栈的分配和释放是按照固定的顺序进行的,不会产生内存碎片。
总体而言,栈的操作速度更快,因为栈的分配和释放由编译器自动管理,不需要额外的内存管理开销。堆的灵活性更高,可以动态分配和释放内存,适用于动态的、大小不确定的数据存储需求。选择使用堆还是栈取决于具体的应用场景和需求。
4. 强制类型转换的使用
强制类型转换(Type Casting)是将一个数据类型转换为另一个数据类型的操作。在某些情况下,我们可能需要将一个数据类型转换为另一个数据类型,以满足特定的需求或进行特定的计算。在C++中,有以下几种类型转换方式:
静态转换(Static Cast):
用法:static_cast<目标类型>(表达式) 用途:用于基本数据类型之间的转换,以及具有继承关系的类之间的转换(向上转型和向下转型)。
动态转换(Dynamic Cast):用法:dynamic_cast<目标类型>(表达式)
用途:用于在具有继承关系的类之间进行安全的向下转型。只能用于指针或引用类型,并且需要有运行时类型信息(需要将基类定义为虚基类)。
常量转换(Const Cast):用法:const_cast<目标类型>(表达式) 用途:用于去除常量属性,可以将 const 或 volatile 修饰的对象转换为非
const 或非 volatile 对象。 重新解释转换(Reinterpret Cast):用法:reinterpret_cast<目标类型>(表达式) 用途:用于进行底层的二进制数据重新解释,将一个指针转换为一个不同类型的指针。
需要注意的是,强制类型转换可能会导致数据丢失或产生未定义的行为,因此在使用时需要谨慎。尽量避免频繁使用强制类型转换,优先考虑设计良好的类层次结构和接口设计来避免类型转换的需求。
5. TCP的三次握手和四次挥手
三次握手(Three-way Handshake):
第一步:客户端向服务器发送一个SYN(同步)包,其中设置了一个初始序列号(ISN)用于后续的数据传输。
第二步:服务器收到客户端的SYN包后,会发送一个SYN+ACK(同步+确认)包作为响应,其中确认号为客户端的ISN加1,并设置自己的初始序列号。
第三步:客户端收到服务器的SYN+ACK包后,会发送一个ACK(确认)包,确认号为服务器的初始序列号加1。
这样,通过三次握手,客户端和服务器都确认了对方的收发能力和序列号的正确性,建立了可靠的连接。
四次挥手(Four-way Handshake):
第一步:客户端发送一个FIN(结束)包,请求关闭连接。
第二步:服务器收到客户端的FIN包后,发送一个ACK包作为确认。
第三步:服务器发送一个FIN包,请求关闭连接。
第四步:客户端收到服务器的FIN包后,发送一个ACK包作为确认。
这样,通过四次挥手,双方都确认了对方的关闭请求和确认,完成了连接的关闭。
三次握手和四次挥手的目的是确保数据的可靠传输和连接的正常关闭。在握手过程中,双方交换信息以建立连接,并在挥手过程中,逐步关闭连接以确保数据的完整性和可靠性。
6. 进程和线程的区别?
进程(Process)和线程(Thread)是操作系统中的两个重要概念,它们具有以下区别:
定义:
进程:进程是正在执行中的程序的实例。它是操作系统资源分配的基本单位,包含程序代码、数据、打开的文件、进程间通信等。
线程:线程是进程中的一个执行单元,是操作系统调度的基本单位。一个进程可以包含多个线程,共享进程的资源。
资源占用:
进程:每个进程都有独立的地址空间、系统资源和文件描述符。进程间通信需要通过显式的机制(如管道、共享内存等)。
线程:线程属于同一进程,共享进程的地址空间、系统资源和文件描述符。线程间通信可以直接读写进程的共享变量。 切换开销:
进程:进程切换需要保存和恢复进程的上下文信息,开销较大。 线程:线程切换只需要保存和恢复线程的上下文信息,开销较小。
并发性:
进程:不同进程之间相互独立,可以并发执行,各自拥有独立的资源。
线程:线程共享进程的资源,可以并发执行,共享数据更方便,但需要注意线程同步和互斥。
创建和销毁:
进程:创建和销毁进程的开销较大,需要操作系统进行资源分配和回收。
线程:创建和销毁线程的开销较小,可以由进程内部进行管理。
总体而言,进程和线程是操作系统中实现并发和多任务的重要概念。进程是资源分配的基本单位,线程是执行调度的基本单位。进程之间相互独立,线程共享进程的资源。选择使用进程还是线程取决于具体的应用场景和需求。
7. 5种IO模型
8. Linux中,显示第二行、最后一行,查找以.c结尾的文件怎么找?
9. git都要用那些命令?回滚是哪个命令?
git add —将该文件添加到暂存区
git status — 命令用于查看在上次提交之后是否有对文件进行再次修改
git diff – 比较文件在暂存区和工作区的差异
git commit — 将暂存区内容添加到本地仓库中
git reset --指定退回某一次提交的版本
git rm – 将文件从暂存区和工作区中删除
git mv --移动或重命名一个文件、目录或软连接
git log --查看历史提交记录
git blame - 以列表形式查看指定文件的历史修改记录
代码拉取与提交:
拉取并同步代码到本地:git pull
将本地所有更改的文件添加到缓存区:git add
添加备注,方便之后查看历史提交记录:git commit -m “本次提交备注信息”
提交到git仓库:git push
关于分支:
查看本地和服务器所有的分支:git branch
创建新分支:git branch [分支名]
切换分支:git checkout [分支名]
合并分支(刚开始不需要):
git checkout master
git merge 分支名称
将本地分支推送到远程仓库:git push -u 远程仓库的别名 本地分支名称:远程分支名称
-u表示把本地分支和远程分支推送到远程仓库,远程仓库的别名一般是origin
10. 分组加条件查询的sql语句怎么写?
语法:
select 分组函数,列(要求出现在group by 后面)from 表【where 筛选条件】group by 分组的列表【order by 子句】
注意:
查询列表必须特殊,要求是分组函数和group by 后出现的字段
11. redis的常用数据类型?顺便说一下具体场景。
Redis常用的数据类型包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)和位图(Bitmap)。
字符串(String):
场景:存储单个值,例如缓存数据、计数器、用户会话等。
哈希(Hash):
场景:存储对象的属性和值,例如存储用户信息、商品信息等。
列表(List):
场景:按照插入顺序存储多个值,可用于实现消息队列、任务队列等。
集合(Set):
场景:存储唯一的、无序的值,可用于实现标签、好友列表、点赞等。
有序集合(Sorted Set):
场景:存储唯一的、有序的值,每个值都关联着一个分数,可用于实现排行榜、时间线等。
位图(Bitmap):
场景:存储位的数据结构,可用于实现用户签到、在线状态等。
这些数据类型的选择取决于具体的应用场景和需求。Redis的数据类型灵活且高效,可以满足不同的数据存储和处理需求。
12. 黑盒测试和白盒测试详解
一、黑盒测试:黑盒测试又称为功能测试,主要检测软件的每一个功能是否能够正常使用。在测试过程中,将程序看成不能打开的黑盒子,不考虑程序内部结构和特性的基础上通过程序接口进行测试,检查程序功能是否按照设计需求以及说明书的规定能够正常打开使用。
测试方法:
1、等价类划分
有效等价类: 是有意义的、合理的输入数据构成的集合。可检查程序是否实现了规格说明中所规定的功能和性能。
无效等价类:检查软件功能和性能的实现是否有不符合规格说明要求的地方。
2、边界值分析法
边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。目的是针对各种边界情况设计测试用例,可以查出更多的错误。
3、判定表方法
判定表能够将复杂的问题按照各种可能的情况全部列举出来,简明并避免遗漏。利用判定表能够设计出完整的测试用例集合。
4、因果图法
定义:多种输入条件的组合,产生多种结果设计测试用例。
设计方法:分析软件规格说明文档描述的哪些是原因(输入条件),哪些是结果(输出条件),给每个原因和结果赋予一个标识符。
之后找出原因与结果,原因与原因之间的对应关系,划出因果图。 在因果图上标上哪些不可能发生的因果关系,表明约束或限制条件。 根据因果图,创建判定表,将复杂的逻辑关系和多种条件组合很具体明确的表示出来。 把判定表的每一列作为依据设计测试用例。
5、场景法
用例场景用来描述流经用例的路径,从用例开始到结束遍历这条路径上所有基本流和备选流。
根据不同的场景设计测试用例,例如用户操作ATM机,就有查询,取款等操作。
二、白盒测试:白盒测试也称为结构测试,主要用于检测软件编码过程中的错误。程序员的编程经验、对编程软件的掌握程度、工作状态等因素都会影响到编程质量,导致代码错误。
测试方法:
白盒测试方法主要分为语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖以及路径覆盖。
语句覆盖:基本思想是设计若干测试用例,运行被测程序,使程序中的每个可执行语句至少被执行一次。
判定覆盖:基本思想是设计若干用例,运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次,即判断真假值均曾被满足。
条件覆盖:基本思想是设计若干测试用例,执行被测程序以后,要使每个判断中每个条件的可能取值至少满足一次。
判定-条件覆盖:是判定和条件覆盖设计方法的交集,即设计足够的测试用例,使得判断条件中的所有条件可能取值至少执行一次,同时,所有判断的可能结果至少执行一次。
条件组合覆盖:基本思想是设计足够的测试用例,使得程序中每个判断的所有可能的条件取值组合都至少出现一次。
路径覆盖:设计测试用例时,覆盖程序中所有可能的执行路径
13. Linux基本命令
mkdir:创建目录
ls:列出目录内容
pwd:显示当前目录
cd:切换目录
touch:创建文件
cp:复制文件或目录
mv:移动文件或目录
rm:删除文件或目录
cat:查看所有内容
nl:查看时显示行号
more:逐页查看
less:逐页查看
head:查看开头部分
tail:查看结尾部分
14. 数据库基本命令
1.数据库的常见操作:
创建数据库
create database 库的名称;
create database 库的名称 default character set utf8;防止中文乱码
create database 库的名称 charset utf8;简写方式展示所有的数据库
show database;删除库
drop database 库的名称;2.表的常见操作:
使用指定的数据库 user 库的名称;创建表 create table [表的名称](字段名称 字段类型(字段长度),字段2,字段3,…)
create table a(name varchar(40),字段2,字段3,…)查看表
show tables;修改表(添加字段)
alter table 表的名称 add column 字段名 字段类型(字段长度);
alter table b add column aaa varchar(40);查看表结构
desc 表的名称;删除表
drop table 表的名称;3.数据的常见操作:
查询表中的数据/记录
select * from 表的名称; *是通配符 意思是查所有插入表中的数据
insert into 表的名称 values(值1,值2,值3,值4) 、
修改表中的数据/记录
update 表名 set 字段名=‘字段的值’;
update b set name=‘nex’ where nex=‘a’ 通常这样写 where 后面添加的是条件 只修改那一行的记录
删除表中的数据/记录
delete from 表的名称; 这是指删除表中所有的记录 一般不用!
delete from 表的名称 where 字段名=‘字段的值’; 这是指删除b表中age=11的那一条记录
15. redis的分布式锁?
16. mysql的索引?
17. 最左前缀匹配原则是什么?具体的数据结构是什么?
18. 讲一下事务和隔离级别以及对应的问题和解决?
19. 讲一下MVCC?
20. mysql里面有哪些锁?
21. 某个sql速度很慢,如何优化?
22. 说一下消息队列?
23. 指针常量和常量指针
常量指针:如果在定义指针变量的时候,数据类型前用const修饰,被定义的指针变量就是指向常量的指针变量,指向常量的指针变量称为常量指针。
格式:(以 int* 指针为例)const int* p(或 int const p); 记忆方法 :看 “ * ” 所在的位置。 常量 指针(指针类型 指针名)
常量指针本质是一个指针,并且这个指针是指向一个常量的,指针指向的地址可以修改,但是内容是不能进行修改的,但可以通过原来指向的变量赋值。
指针常量: int const p 指针类型 常量 指针名
指针常量本质就是一个常量,而用指针去修饰,指针指向的地址不可以进行修改,但是指针指向地址的内容是可以进行修改的,可以通过指针赋值,也可以通过原来指向的变量进行赋值。
24. 数据库中delete和trancate区别
delete 只删除记录 truncate是先摧毁表再重建表
25. C++是如何解决内存泄漏的?
26. 了解哪些排序方法?简述下快速排序。
27. 数据库三大范式。
定义:
数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。
各范式特点:
第一范式:保证列的原子性,保证列不可再分。
第二范式:唯一性 ;一个表只说明一个事物;有主键且非主键依赖主键;(限制多对多的关系,建立一个关联表,通过外键和联合主键来关联两张表)
第三范式:每列都与主键有直接关系,不存在传递依赖;(限制一对多的关系,在从表中建立一个外键,通过外键来引用主表的信息)
PS:第二范式要遵循第一范式,第三范式要遵循第二范式。
28. 虚函数和纯虚函数的区别?
虚函数和纯虚函数的区别:
虚函数和纯虚函数可以出现在同一个类中,该类称为抽象基类。(含有纯虚函数的类称为抽象基类)
1、在使用方式上有所不同:虚函数可以直接使用,纯虚函数必须在派生类中实现后才能使用;
2、在定义上有所不同:虚函数在定义时在普通函数的基础上加上 virtual 关键字,纯虚函数定义时除了加上virtual 关键字还需要加上 =0;
3、虚函数必须实现,不然编译器会报错;
对于实现纯虚函数的派生类,该纯虚函数在派生类中被称为虚函数,虚函数和纯虚函数都可以在派生类中重写;
析构函数最好定义为虚函数,特别是对于含有继承关系的类(析构函数被定义为虚函数之后,在使用指针引用时可以动态绑定,实现运行时的多态,保证使用基类指针就能够调用适当的虚构函数针对不同的对象进行清理工作。);析构函数可以定义为纯虚函数,此时,其所在的类为抽象基类,不能创建实例化对象