C++软件工程师面试考察主要有C++基础(最好也懂Java)、数据结构及简单算法、TCP、操作系统、网络编程、Linux基本操作和Shell编程、数据库,设计模式和智力题也会涉及少量。
参考资料:《Effective C++》、《C++ Prime》、《STL源码剖析》
static修饰全局函数有什么作用?
限制他的作用域只能在本文件之内。
extern关键字作用
声明一个外部变量。
const关键字的作用
本质:define只是字符串替换,inline由编译器控制,具体的:
12. 内联函数在编译时展开,而宏是由预处理器对宏进行展开
13. 内联函数会检查参数类型,宏定义不检查函数参数 ,所以内联函数更安全。
14. 宏不是函数,而inline函数是函数
15. 宏在定义时要小心处理宏参数,(一般情况是把参数用括弧括起来)。
宏 | 描述 |
---|---|
_LINE_ | 这会在程序编译时包含当前行号 |
_FILE_ | 这会在程序编译时包含当前文件名 |
_DATE_ | 这会包含一个形式为 month/day/year |
_TIME_ | 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 |
#include
using namespace std;
int main ()
{
cout << "Value of __LINE__ : " << __LINE__ << endl;
cout << "Value of __FILE__ : " << __FILE__ << endl;
cout << "Value of __DATE__ : " << __DATE__ << endl;
cout << "Value of __TIME__ : " << __TIME__ << endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48
C++构造函数能抛异常吗?析构呢?
不能。
C++/C内存分配方式,堆与栈的区别
构造函数为什么不能定义为虚函数,析构函数为什么可以?
内存泄漏类型:
检测内存泄漏:
全局变量和局部变量的区别
C++智能指针
C++11新特性
纯虚函数的作用和实现方式
STL源码、vector、list、map、set
字节对齐的原则
空结构体的sizeof()返回值
答案是1
静态连接与动态链接的区别
多态是什么?举一个多态的例子
多态性与虚函数表
静态多态和动态多态
多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。
重写、重载与隐藏的区别
重载的函数都是在类内的。只有参数类型或者参数个数不同,重载不关心返回值的类型。
覆盖(重写)派生类中重新定义的函数,其函数名,返回值类型,参数列表都跟基类函数相同,并且基类函数前加了virtual关键字。
隐藏是指派生类的函数屏蔽了与其同名的基类函数,注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。有两种情况:(1)参数列表不同,不管有无virtual关键字,都是隐藏;(2)参数列表相同,但是无virtual关键字,也是隐藏。
构造函数为什么不能定义为虚函数,析构函数为什么可以
#include
using namespace std;
class TestClass{
public:
TestClass();
};
TestClass::TestClass(){
cout<<"TestClass"<<endl;
}
TestClass Ts;//定义个全局变量,让类里面的代码在main之前执行
int main(){
cout<<"main"<<endl;
return 0;
}
输出为
TestClass
main
#include
void static __attribute__((constructor)) before_main(){
printf("before main\n");
}
void static __attribute__((destructor)) after_main(){
printf("after main\n");
}
int main(int argc, char const *argv[]){
printf("main\n");
return 0;
}
程序结果为
before main
main
after main
#include
using namespace std;
// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
// 第二个命名空间
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
int main ()
{
// 调用第一个命名空间中的函数
first_space::func();
// 调用第二个命名空间中的函数
second_space::func();
return 0;
}
C++多线程
explict关键字的作用
参考资料:《大话数据结构》、《数据结构浙大版》、《算法设计与分析》、《算法导论》、《剑指offer》、《LeetCode》、《组合数学》
/*快速排序函数*/
//输入:待排序的数组,排序其实位置>=0,排序终止位置<=length(a) - 1
void QuickSortHelper(ElemType a[],int low,int high)
{
if(low >= high)
return;
ElemType temp = a[low]; //存储序列首位元素
int i = low + 1;
int j = high;
while(i != j)
{
while(i < j && a[j] > temp)
j--;
while(i < j && a[i] < temp)
i++;
//交换两个元素
if(i < j)
{
swap(a,i,j);
}
}
a[low] = a[i];
a[i] = temp;
QuickSortHelper(a,low,i -1);
QuickSortHelper(a,i + 1,high);
}
/*快速排序*/
void QuickSort(ElemType a[], int n)
{
QuickSortHelper(a,0,n-1);
}
/*两个有序子列的合并*/
void Merge(ElemType a[], ElemType temp[], int left_begin, int right_begin, int right_end)
{
int left_end = right_begin - 1;
int i = left_begin; //存放结果数组的初始位置
int num_element = right_end - left_begin + 1;
//进行合并
while (left_begin <= left_end && right_begin <= right_end)
{
if (a[left_begin] <= a[right_begin])
temp[i++] = a[left_begin++];
else
temp[i++] = a[right_begin++];
}
while (left_begin <= left_end)
temp[i++] = a[left_begin++];
while (right_begin <= right_end)
temp[i++] = a[right_begin++];
//改变a中对应段的值
for (int j = 0; j < num_element; j++)
{
a[right_end] = temp[right_end];
right_end--;
}
}
/*递归进行*/
void MSort(ElemType a[], ElemType temp[], int begin, int end)
{
int mid;
if (begin < end)
{
mid = (begin + end) / 2;
MSort(a, temp, begin, mid);
MSort(a, temp, mid + 1, end);
Merge(a, temp, begin, mid + 1, end);
}
}
/*归并排序*/
void MergeSort(ElemType a[], int n)
{
ElemType *temp = (int *)malloc(sizeof(int) * n);
if (temp != NULL)
{
MSort(a, temp, 0, n - 1);
free(temp);
}
else
cout << "空间不足!" << endl;
}
/*调整为最大堆*/
void PercDown(ElemType a[], int p, int n){
// 将N个元素的数组中以a[p]为根的子堆调整为关于a[i]的最小堆
int parent, child;
int x;
x = a[p]; //取出根节点的值
for (parent = p; (parent * 2 + 1) < n; parent = child){
child = parent * 2 + 1;
if ((child != n - 1) && (a[child] < a[child + 1]))
child++;
if (x >= a[child])
break;
else
a[parent] = a[child];
}
a[parent] = x;
}
/*堆排序*/
void HeapSort(ElemType a[], int n)
{
for (int i = n / 2; i >= 0; i--)
PercDown(a, i, n); //建立一个最大堆
for (int i = n - 1; i > 0; i--)
{
Swap(a, 0, i); //交换最大最小元素,把最大元素给a[i]
PercDown(a, 0, i); //剩下的i个元素调整为最大堆
}
}
void shuffle(int cards[],int n)
{
if(cards==NULL)
return ;
srand(time(0));
for(int i=0;i<n-1;++i)
{
//保证每次第i位的值不会涉及到第i位以前
int index=i+rand()%(n-i);
int temp=cards[i];
cards[i]=cards[index];
cards[index]=temp;
}
}
应用层 |
---|
表示层 |
会话层 |
运输层 |
网络层 |
数据链路层 |
物理层 |
需要三次的原因:防止已失效的报文段出现在本连接中。
为什么TCP建立连接需要三次握手,而断开连接需要四次挥手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
TIME_WAIT的意义,为什么等于2MSL?
MSL是最长报文段寿命,设置的目的是:
TCP头部校验的原理,安全吗,可以仿造吗
TCP校验和是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。
TCP、UDP的区别?服务器和客户端建立的过程?
TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能顺序地从一端传到另一端。
UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,不保证数据按顺序传递,故而传输速度很快。
UDP编程的服务器端一般步骤
UDP编程的客户端一般步骤是
TCP编程的服务器端一般步骤是
TCP编程的客户端一般步骤是
socket中的close是一次就关闭的吗?半关闭状态是怎么产生的?
不是,当A发送给B控制FIN的时候,A到B这个方向的连接就关闭了,这个时候处于半关闭的状态,但是B到A这个方向的连接并没有关闭,因为B要等到将数据全部发送完毕之后才会发送FIN给A。
TCP拥塞控制
重点掌握慢开始、拥塞避免、快重传、快恢复。
TCP流量控制,采用滑动窗口会用什么问题?
流量控制是为了让发送方的发送速率不要太快,要让接收方来得及接收。
Nagle算法:①当发送方首都哦啊哦对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到到达的数据进行缓存。②当到达的数据已达到发送窗口大小的一半或已达到报文段的长度的时候就立即发送一个报文段。
糊涂窗口综合征:就是由于发送端和接收端上的处理不一致,导致网络上产生很多的小包,结果报文段包含了一个大大的头部,携带数据很少。数据传输效率低。处理方法是等待窗口大小满足一定的条件之后(能够接收一个最大报文,或者缓冲区的一半),再来发送窗口通告,这样就不会产生小报文。
滑动窗口机制为端到端设备间的数据传输提供了可靠的流量控制机制。然而,它只能在源端设备和目的端设备起作用,当网络中间设备(例如路由器等)发生拥塞时,滑动窗口机制将不起作用。
拥塞控制和流量控制的区别?
http协议与TCP协议的联系
TPC协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。
http/1.0和http/1.1的区别?
(1)根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的
(2)根据HTTP规范,POST表示可能修改变服务器上的资源的请求
http状态码
http和https的区别?由http升级到https需要哪些操作?
HTTP 指的是超文本传输协议,https 指的是超文本传输安全协议。HTTPS 就是将 HTTP 中的传输内容进行了加密,然后通过可靠的连接,传输到对方的机器上。加密的协议是 TLS,其前身是 SSL。
https具体怎么实现?,怎么确保安全性?
http中浏览器一个URL的流程,这个过程中浏览器做些什么,URL包括哪三个部分?
网页解析的过程
一个机器能使用的端口号上限是多少?为什么?可以改变吗?如果想要用的端口超过这个限制怎么办?
端口号最多是65535个,端口号2个字节,16位,所以最大表示65535.不能改变
对称加密和非对称加密
对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
数字证书的了解(高频)
客户端为什么信任第三方证书?
RSA加密算法,MD5原理
单条记录高并发访问的优化
介绍一下ping的过程,分别用到了那些协议?
ping用来测试两台主机之间的连通性。ICMP协议
TCP/IP分片粘包过程
有没有抓过TCP包,描述一下
一个IP配置多个域名,靠什么识别?
主机头
路由器的工作原理和作用,交换机的工作原理和作用
对路由协议的了解与介绍。内部网关协议IGP包括RIP,OSPF和外网网关协议EGP和BGP
路由协议使用的算法
服务器攻击(DDos攻击)
什么是临界区?进程进入临界区的调度原则是?
临界区是一段对共享资源的保护代码,该保护代码在任意时刻只允许一个线程对共享资源访问。
进程进入临界区的调度原则是:
①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
互斥对象、临界区和事件的区别?
互斥是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。
事件对象也是属于内核对象,它的主要成员包括:1.使用计数 2.指明该事件是一个自动重置事件还是一个人工重置事件的布尔值3.指明该事件处于已通知状态还是未通知状态的布尔值。
互斥对象、事件对象与临界区的比较:
进程和程序的区别
一个进程可以创建多少个线程?和什么有关?
一个进程可以创建的线程数由可用虚拟空间和线程的栈的大小共同决定
僵尸进程?
什么是死锁?死锁产生的原因?死锁四个必要条件?死锁的解除、死锁控制?
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
死锁产生的原因
系统资源的竞争
系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
进程运行推进顺序不合适
进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。
死锁的四个条件
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的避免与预防
死锁避免的基本思想
系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何让这四个必要条件不成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
死锁避免和死锁预防的区别
死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现,而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁。死锁避免是在系统运行过程中注意避免死锁的最终发生。
死锁的解除
一旦检测出死锁,就应立即釆取相应的措施,以解除死锁。死锁解除的主要两种方法:
抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以解除死锁状态。
终止(或撤销)进程。终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态解脱出来。
头文件在编译过程中的作用?(网易游戏)
头文件并不参加链接和编译。编译器第一步要做的就是简单的把头文件在包含它的源文件中展开。不知你是否能理解这句话。也就是头文件里面有什么内容,通通把它移到包含这个头文件的源文件里。(我觉得这是个很重要的概念,可以帮助我们简化理解编译链接的过程,包括理解头文件中定义静态变量或静态函数是怎么回事)。编译器经过这一步转换后剩下什么呢?就是一堆cpp文件了。而头文件已经不再是编译器需要关心的东西了。编译器接下来就要处理这一堆cpp文件了。
所以第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。 第二个阶段编译、优化阶段。
为何不能在头文件中定义?
防止多重定义。
进程间通信的方法?
线程创建的方式有几种?
进程调度算法?
布隆过滤器的优点与缺点
布隆过滤器处理大规模问题时的持久化,包括内存大小首先、磁盘换入换出问题
文件读写使用的系统调用
线程池的了解、优点、调度处理方式和保护任务队列的方式
于是为了避免一个程序需要大量创建线程时的不必要浪费,也就是最好的去避免线程创建与线程销毁的时间浪费,此时线程池就出现了。线程池的实现就是在初始的时候创建一些线程(业界通常认为创建CPU核心数的两倍为最佳,也有说是两倍+1),创建的线程为挂起状态(就绪),当我们有任务要处理的时候,我们就激活一个就绪的线程去完成任务,完成任务后,线程又变为就绪态进行继续等待任务的到来。这样过程使得每个线程一次创建,多次使用,如果你的程序并没有多次任务处理,使得线程池中的线程长时间处于就绪态,此时就建议你直接使用一个线程就好,不必使用线程池。
线程池怎么创建?
怎么回收线程
多线程同步(项目中可能会问)
mencache
异常和中断的区别
如何保证线程安全?
Linux进程管理
Linux内核的进程调度
fork返回值是什么?
什么是虚拟内存?
文件系统的理解(EXT4,XFS,BTRFS)
Linux的内存管理?
Linux基本命令?
命令 | 作用 |
---|---|
pwd | 显示当前目录 |
rm | 删除 |
touch | 生成文件 |
cat | 读取指定文件的内容并打印到终端输出 |
mkdir | 新建目录make directory |
file | 查看文件类型 |
whereis,which,find 和 locate | 查找 |
chown | 改变文件所有者 |
df | 查看磁盘容量 |
wc | 计数工具 |
tr | 删除一段文本信息中的某些文字。或者将其进行转换 |
join | 连接两个文件 |
paste | 它是在不对比数据的情况下,简单地将多个文件合并一起,以Tab隔开 |
为什么使用IO多路复用,最主要的原因是什么?
epoll有两种触发模式?这两种触发模式有什么区别?编程的时候有什么区别?
上一题中编程的时候有什么区别,是在边缘触发的时候要把套接字中的数据读干净,那么当有多个套接字时,在读的套接字一直不停的有数据到达,如何保证其他套接字不被饿死(面试网易游戏的时候问的一个问题,答不上来,印象贼深刻)。
GDB调试
Linux进程和线程如何创建、退出?进程退出的时候,自己没有释放的资源(如内存没有free)会怎样?
索引
索引(Index)是帮助MySQL高效获取数据的数据结构;在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,可以在这些数据结构上实现高级查找算法,提高查询速度,这种数据结构,就是索引。
B-Tree 索引:最常见的索引类型,大部分引擎都支持B树索引。
HASH 索引:只有Memory引擎支持,使用场景简单。
R-Tree 索引(空间索引):空间索引是MyISAM的一种特殊索引类型,主要用于地理空间数据类型。
Full-text (全文索引):全文索引也是MyISAM的一种特殊索引类型,主要用于全文索引,InnoDB从MySQL5.6版本提供对全文索引的支持。
事物是什么?
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,由一条或者多条sql语句组成,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
acid特性?
参考资料:《设计模式之禅》,简要了解一下即可