2020秋招_C++开发面试记录-1


每天一个linux命令-学习

理一理字节对齐的那些事
六道腾讯、百度、美团常爱问的面试智力题和答案

shell获取文件最后一行

awk 'END {print}' a.log
sed -n '$p' a.log
tail -n 1 a.log   # -n后的参数表示输出倒数几行,文件很大的时候用tail效率高

百度(开发测试岗位)

一面

自我介绍
为什么想转行互联网?为什么选择北京的岗位?
为什么不留在华为?

网络七层模型,TCP、UDP在哪一层,区别?
最主要区别:TCP是面向连接的可靠的,UDP是无连接的不可靠的。
TCP连接为什么可靠?
TCP/IP可靠传输的基础是滑动窗口协议和连续ARQ(自动重传请求)协议,配合着流量控制和拥塞控制,使得整个传输过程保证:

  1. 传输信道不产生差错;
  2. 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据(通过累计确认、超时重传、流量控制、拥塞控制等保证)。

如何确认的? -> 连续ARQ协议
TCP建立连接的过程? 三次握手

用过哪些STL标准库 -> C++标准模板库的核心:容器、算法、迭代器。string、vector、stack、queue、map、set、list。模板编程,实现参数类型的泛化。
深拷贝和浅拷贝的区别 -> 浅拷贝只复制了对象,未复制对象中动态分配的内存。(浅拷贝只复制了对象中的指针,深拷贝会复制指针指向的动态内存)浅拷贝在对象析构的时候会出现野指针的现象,造成内存的二次释放异常。
静态库和动态库的区别 (没回答对) 静态库和动态库的区别
静态库在程序编译时就会被连接到目标文件中生成可执行文件,每个进程都会复制一份,因此静态库在内存中存在多分拷贝;动态库在运行的时候才载入,内存中只有一份,被进程之间所共享。
进程和线程的区别
死锁产生的条件
死锁产生的四个必要条件:

  1. 互斥:至少有一个资源一次只能被一个进程使用。
  2. 占有并等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 (这个条件忘记了)
  3. 非抢占式:进程已获得的资源在未使用完之前不能被抢占。
  4. 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系。

linux查看文件的前几行,后几行,中间几行,前几列? head tail sed awk cat grep
linux查看文件前几行和后几行的命令

  • 前几行:head -n <行数> <文件>
  • 后几行:tail -n <行数> <文件>
  • 中间几行:
    【一】从第3000行开始,显示1000行。即显示3000~3999行
    cat filename | tail -n +3000 | head -n 1000
    【二】显示1000行到3000行
    cat filename| head -n 3000 | tail -n +1000
    PS:注意两种方法的顺序
    分解:
    tail -n 1000:显示最后1000行
    tail -n +1000:从1000行开始显示,显示1000行以后的
    head -n 1000:显示前面1000行
    将内容输出到/home/test文件中
    ‘head -n 10 /etc/profile >>/home/test’
    ‘tail -n 5 /etc/profile>>/home/test’
    【三】用sed命令
    sed -n '5,10p' filename这样你就可以只查看文件的第5行到第10行。
  • 前几列:
    awk '{print $1,$2}' ./in.txt > ./out.txt,提取in.txt中第一、二列(以空格分隔)到out.txt中
  • awk:
    1、打印文件的第一列(域) : awk ‘{print $1}’ filename
    2、打印文件的前两列(域) : awk ‘{print $1,$2}’ filename
    3、打印完第一列,然后打印第二列 : awk ‘{print $1 $2}’ filename
    4、打印文本文件的总行数 : awk ‘END{print NR}’ filename
    5、打印文本第一行 :awk ‘NR==1{print}’ filename
    6、打印文本第二行第一列 :sed -n “2, 1p” filename | awk ‘print $1’
  • awk、sed、grep更适合的方向: Linux awk 命令
    grep 更适合单纯的查找或匹配文本
    sed 更适合编辑匹配到的文本
    awk 更适合格式化文本,对文本进行较复杂格式处理

数据库查询语句 -> 不会…

手撕代码:
跳台阶、判断回文串、数组中出现次数最多的数(考虑不够全面,可能不止一个)

测试相关概念 讲了华为的实习经历

二面

linux 查找目录下大于一定大小的文件?
Linux查找某个大小范围内的文件

手撕代码:
4. 寻找两个正序数组的中位数


腾讯PCG

初试 45分钟

自我介绍
有上过哪些相关的课程
进程和线程的区别
进程间通信的方式
管道及命名管道、信号、 消息队列、共享内存、信号量、套接字

什么是消息队列?
消息队列本身是异步的,它允许接收者在消息发送很长时间后再取回消息,这和大多数通信协议是不同的。例如WWW中使用的HTTP协议(HTTP/2之前)是同步的,因为客户端在发出请求后必须等待服务器回应。然而,很多情况下我们需要异步的通信协议。比如,一个进程通知另一个进程发生了一个事件,但不需要等待回应。但消息队列的异步特点,也造成了一个缺点,就是接收者必须轮询消息队列,才能收到最近的消息。

进程间的通信方式(一):共享内存
共享内存实际上就是进程通过调用shmget(Shared Memory GET 获取共享内存)来分配一个共享内存块,然后每个进程通过shmat(Shared Memory Attach 绑定到共享内存块),将进程的逻辑虚拟地址空间指向共享内存块中。
共享内存的特点:

  • 共享内存是进程间共享数据的一种最快的方法。一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
  • 使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

信号量是一个计数器,可以用来控制多个进程对共享资源的访问(如共享内存)。
管道和命名管道的区别
管道只能承载无格式的字节流,速度慢,容量有限,只有父子进程之间能通信。命名管道具有了名字,可以在不是父子关系的进程之间通信。

PS:在看进程间通信看到消息队列和管道的区别时,里面说,消息队列克服了管道只能承载无格式字节流的问题,我想问一下无格式字节流是什么意思?
消息队列传递的消息都是有一定的格式的,比如它的前4个字节可能表示消息类型,后面可能还会有消息长度,等等,而管道不需要,你写入什么,写入多少,全由你自己定义。

流量控制和拥塞控制

同步IO和异步IO的区别

手撕代码:
4. 寻找两个正序数组的中位数 ->只写出了O(m+n)时间复杂度和O(m+n)空间复杂度的解法。然后优化空间复杂度为O(1),运行一直不太对…

智力题:
一共100本书,A、B轮流拿,每次只能拿1~5本,A先拿,怎么样才能保证A能拿到最后一本书。
思路:A倒数第二次拿完后剩下6本书,然后B拿x本书,A就拿6-x本书。反推上去,每次A拿完给B剩下6的倍数本书,主动权就掌握在A手上。因此,A应该先拿走4本书。


shopee

一面

自我介绍,然后问了下项目,然后根据项目问知识点。

语言特性

C++内存泄露怎么造成的?如何解决?

  1. 堆内存泄漏,Heap leak。堆内存是指程序运行中根据需要分配通过malloc/realloc/new等从堆中分配的一块内存,在完成后必须通过调用对应的 free 或者 delete 释放掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生 Heap Leak。
  2. 系统资源泄露,Resource Leak。主要指程序使用系统分配的资源比如 Bitmap,handle,socket等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。

使用智能指针防止堆内存泄露。

智能指针有哪些?区别?具体原理?

数据结构和算法

对背包问题的理解?

树的搜索算法,解释下区别? -> dfs和bfs (面试的时候答成了二叉树的遍历方式,前序、中序、后序、层序)

快速排序原理讲一下?决定时间复杂度的关键是什么?
决定时间复杂度的关键是基准的选择,如果每次选择的基准值位于数组的中位数,则时间复杂度O(nlogn);如果每次选择的基准值为数组的最小值或者最大值,则时间复杂度会变成O(n)。

PS:快排的优化

  • 基准选择方法:三数取中法;-> 对升序或者降序数组效率提升较大(避免了形成单斜树的情况)。
  • 在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割;-> 数组中有大量重复元素的情况有效率有显著的提升。

网络

DNS解析地址的过程? -> 域名与IP地址之间的映射
DNS是应用层协议
DNS解析过程原理

  • 递归查询。
  • 迭代查询。
    DNS查询

数据链路层传输的单位? 数据帧

传输单位
传输层 报文(网络中交换与传输的数据单元)
网路层 数据包,包含报文,并增加IP地址
数据链路层 数据帧,包含数据包,并增加相应的MAC地址(物理地址)
物理层 比特(bit)

网络OSI七层模型?网络层是什么协议?IP协议的作用?
IP协议,路由。

传输层的协议?TCP和UDP的区别?

数据路相关

mysql数据库的两个引擎?区别?

数据库中数据加载本地的数据到内存用hash_map保存,当数据量很大的时候怎么办?
mysql底层用B+树,减少了磁盘的IO次数。加载到内存用哈希表保存可以加快访问的速度。但是数据量很大的时候,内存可能会放不下。可以参考页面置换算法中LRU算法,即最近使用的数据在未来一段时间内仍然放在内存中被使用。…可以去代码中实现下!!

操作系统

操作系统的最小调度单位是什么?资源分配的最小单元?
线程;进程。
进程是操作系统资源分配(包括CPU、内存、磁盘IO等)的最小单元;线程是CPU调度和分配的基本单元,是最小的执行单元。


字节-飞书

一面

首先,web服务器项目提问(15分钟)
I/O多路复用的实现过程?有什么优势? -> web服务器中使用epoll
select/poll/epoll区别:使用select/poll,程序每次从内核得到的是所有n个事件的集合,然后轮询查看n个事件,处理就绪的k个事件,O(n);epoll利用红黑树和就绪链表以及内核注册回调函数的机制,程序每次从内核得到的是k个就绪事件,直接可以轮询并处理这k个就绪事件,O(k)。
epoll实现原理:epoll在内核维护一个红黑树和一个就绪链表(以空间换时间),红黑树存储所有文件描述符相关数据,就绪链表存储就绪的文件描述符相关数据。红黑树上每个节点在内核注册了回调函数,一旦检测到某个文件描述符就绪时,就触发回调函数,将节点插入就绪链表。epoll_wait()会将就绪链表中的数据拷贝到用户空间(代码中参数events用来从内核得到就绪事件的集合),然后清空就绪链表。
PS:文件描述符就绪是指recv时,内核缓冲区准备好读的数据;send时,内核缓冲区准备好写的数据;accept时,连接队列中存在已经完成的连接。

// WebServer::eventListen()
m_epollfd = epoll_create(5);
assert(m_epollfd != -1);
// WebServer::eventLoop()
while (!stop_server){
    
    
	int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);
	// EINTR:epoll_wait操作被信号中断时返回。此时只需重启epoll_wait。
	if (number < 0 && errno != EINTR){
    
    
	    LOG_ERROR("%s", "epoll failure");
	    break;
	}
	......
}

I/O多路复用的优势并不是对于单个连接能处理的更快,而是在于可以在单个线程/进程中处理更多的连接

为什么设置非阻塞的socket? -> web服务器中客户端的epollfd设置成ET模式时,必须用非阻塞的socket。

  • LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
  • ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

设置非阻塞的socket后,需要循环读取和写入,保证一次处理完。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
PS:LT模式下socket可以设置成阻塞模式也可以设置成非阻塞模式。

int setnonblocking(int fd)  // //对文件描述符设置非阻塞
{
    
    
    int old_option = fcntl(fd, F_GETFL); //fcntl针对(文件)描述符提供控制
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}
bool http_conn::read_once()
{
    
    
    if (m_read_idx >= READ_BUFFER_SIZE)
    {
    
    
        return false;
    }
    int bytes_read = 0;
    while (true)
    {
    
    
        bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);
        if (bytes_read == -1) 
        {
    
    
            // 错误EAGAIN提示recv现在没有数据可读,请稍后再试
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                break;
            return false;
        }
        else if (bytes_read == 0) // 连接关闭
        {
    
    
            return false;
        }
        m_read_idx += bytes_read; // bytes_read > 0为接收到的数据大小
    }
    return true;
}

GET和POST的区别?

http和https的区别?https连接怎么实现?
HTTPS是HTTP协议的安全版本,HTTP协议的数据传输是明文的,是不安全的,HTTPS使用了SSL/TLS协议进行了加密处理。
http和https使用连接方式不同,默认端口也不一样,http是80,https是443。
https的ssl连接过程 -> 通常HTTP直接和TCP通信。当使用SSL时,则演变成先和SSL通信,再由SSL和TCP通信。

  1. 客户端发送https请求;
  2. 服务端接收到请求后返回公钥;
  3. 客户端产生随机密钥,并用公钥对密钥加密,然后向服务端发送加密后的密钥;
  4. 收到服务端确认后,客户端向服务端发送加密后的密文;
  5. 服务端通过密钥解密客户端加密后的密文,并返回处理状态。

服务器响应HTTP请求返回的状态有哪些? -> web服务器中返回200(ok)、500(服务器内部错误)、400(请求报文语法错误)、403(没有权限获取资源)、404(没有找到资源)

  • 1xx 信息状态码,接收的请求正在处理
  • 2xx 成功状态码,请求正常处理完毕
  • 3xx 重定向状态码,需要进行附加操作以完成请求
  • 4xx 客户端错误状态码,服务器无法处理请求
  • 5xx 服务端错误状态码,服务器处理请求错误

进程和线程的区别?

怎么关闭服务器? -> ctrl + c
ctrl+c怎么结束服务器的? -> 发送 SIGINT 信号结束前台进程
kill结束进程的实现原理? -> kill <PID>,SIGTERM信号结束进程;kill -9 <PID>,SIGKILL信号强制结束进程(SIGKILL信号不会被捕获)。
kill进程与ctrl+c的区别?
Linux关闭与切换进程相关的信号:SIGINT、SIGKILL、SIGTERM、SIGSTOP

如何关闭服务器连接? -> setsocketopt设置O_LINGER选项,在close(socket)调用时,但是还有数据没发送完毕的时候容许逗留。(等数据发送完毕才关闭连接)

    if (0 == m_OPT_LINGER) adjust
    {
    
    
        /* Structure used to manipulate the SO_LINGER option.  
            struct linger
            {
                int l_onoff;		// Nonzero to linger on close. 
                int l_linger;		// Time to linger.  
            };
        */
        struct linger tmp = {
    
    0, 1}; // l_onoff置‘0’表示在close(socket)调用时不逗留
        setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp)); // SO_LINGER -> 延迟关闭连接, (linger:逗留)
    }
    else if (1 == m_OPT_LINGER) // 在close(socket)调用,但是还有数据没发送完毕的时候容许逗留
    {
    
    
        struct linger tmp = {
    
    1, 1};
        setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
    }

事务隔离级别?四种隔离级别有什么区别?

然后,写代码两题(30分钟): 注意要考虑边界条件!可以最先把边界条件写上!!
106. 从中序与后序遍历序列构造二叉树

TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    
    
    return traverse(inorder,0,inorder.size()-1, postorder,postorder.size()-1);
}

TreeNode* traverse(vector<int>& inorder,int ins,int ine, vector<int>& postorder, int pe){
    
    
    if(ins > ine){
    
    
        return nullptr;
    }

    int target = postorder[pe];
    int mid = -1;
    TreeNode* root = new TreeNode(target);
    for(int i = ins; i <= ine; i++){
    
    
        if(inorder[i]==target){
    
    
            mid = i;
            break;
        }
    }
    root->left =  traverse(inorder,ins,mid-1,postorder,pe-1-(ine-mid));
    root->right = traverse(inorder,mid+1,ine,postorder,pe-1);

    return root;
}
``
[153. 寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/) -> 二分查找
```cpp
    int findMin(vector<int>& nums) {
    
    
        int n = nums.size()-1;
        if(nums[0] < nums[n]){
    
     // 没有进行旋转的情况
            return nums[0];
        }
        int l = 0, r = nums.size()-1;
        // k作为一个比较的标准,来判断查找应该在左区间还是右区间
        int k = nums[nums.size()-1], re = -1; 
        while(l < r){
    
    
            int mid = l + (r-l)/2;
            if(nums[mid] > k){
    
    
                l = mid + 1;
            }else{
    
    
                if(nums[mid] < nums[mid-1]){
    
     // 此处mid-1不能是没有进行旋转的情况
                    re = nums[mid];
                    break;
                }
                r = mid - 1;
            }
        }
        // 最后l==r没有进行nums[mid] < nums[mid-1]的判断
        return re == -1 ? nums[l] : re;
    }

大华实习

电话一面

主要问了C++基础和网络通讯:

ping命令使用的tcp报文还是udp报文呢?
答:ping命令使用的是ICMP报文,ICMP报文封装在ip包里。ICMP协议也是tcp/ip协议族中的一个子协议,所以从这一层来看,icmp报文和tcp报文,udp报文是同一个级别。所以ping命令使用的报文既不是tcp报文也不是udp报文。

Ping命令用ICMP实现的,ICMP是Internet控制消息协议,用于IP主机,路由器之间传递消息。控制消息是指网络通不通,主机是否可达,路由器是否可用等网络本身的消息,这些控制消息并不传输用户数据。

Ping的原理是:向指定IP发送一定长度的数据包,按照约定,若指定IP存在的话,会返回同样大小的数据包,若没有在特定时间返回就是超时,就认为指定IP不存在。但是由于防火墙会屏蔽ICMP协议,所以ping不通不一定说明对方IP不存在。
注:跟踪路由的Tracert命令也是基于ICMP协议

linux下网络相关命令?

linux网络相关命令分为:监测,传输,远端登录

  1. ping - 监测网络连通性
  2. route - 显示和操作IP路由表
  3. traceroute - 打印数据包的路由信息
  4. netstat - 查看网络状态、端口状态
  5. ftp - 文件传输
  6. wget - 网络下载器
  7. ssh - 远程登录

netstat命令是一个监控TCP/IP网络的非常有用的工具,netstat命令的功能是显示网络连接、路由表和网络接口信息,可以让用户得知目前都有哪些网络连接正在运作。

该命令的一般格式为:
netstat [选项]
命令中各选项的含义如下:
-a 显示所有socket,包括正在监听的。
-c 每隔1秒就重新显示一遍,直到用户中断它。
-i 显示所有网络接口的信息,格式同“ifconfig -e”。
-n 以网络IP地址代替名称,显示出网络连接情形。
-r 显示核心路由表,格式同“route -e”。
-t 显示TCP协议的连接情况。
-u 显示UDP协议的连接情况。
-v 显示正在进行的工作。

linux cat、awk指令?
每天一个linux命令(10):cat 命令

  • cat命令的用途是连接文件或标准输入并打印。这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用。
    命令格式:
cat  [选项]  [文件] ...

Linux awk 命令

  • AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。

c++的静态类成员需要初始化吗? -> 可以不初始化,但是一定要定义,即定义了才分配内存。在类里面声明,类外面定义。
我们知道C++类的静态成员变量是需要初始化的,但为什么要初始化呢?其实这句话“静态成员变量是需要初始化的”是有一定问题的,应该说“静态成员变量需要定义”才是准确的,而不是初始化。两者的区别在于:初始化是赋一个初始值,而定义是分配内存。静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义,实际上是给静态成员变量分配内存。

//test.cpp   
#include <stdio.h>   
class A {
    
       
    public:   
        static int a; //声明但未定义  
 };   
int A::a = 3; //定义了静态成员变量,同时初始化。也可以写"int A::a;",即不给初值,同样可以通过编译,默认值为0  
int main() {
    
       
    printf("%d", A::a);  
    return 0;  
}  

c++类静态成员和普通成员的区别?

  • 类的静态成员(变量和方法)属于类本身,在编译的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
  • C++会区分两种类型的成员函数:静态成员函数和非静态成员函数。这两者之间的一个重大区别是,静态成员函数不接受隐含的this指针。所以,它就无法访问自己类的非静态成员。

c++引用和指针的区别?引用有什么好处? -> 指针存在空指针、野指针的情况,不安全
引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。

虚函数的作用? -> 使指向动态内存的指针在编译时和运行时的类型不同,实现多态。
当使用类的指针调用成员函数时,普通函数由指针类型决定 ,虚函数由指针指向的实际对象类型决定!因此虚函数可以实现编译时(基类类型)和运行时(派生类类型)的类型不同,从而实现多态。普通函数无法实现多态。虚函数指针是对象私有,在堆内存中,虚函数指针指向全局数据区的虚函数表,再通过虚函数表找到代码区中虚函数的地址。因此虚函数由指针指向的实际对象类型决定。

基类的析构函数为什么要设置成虚函数? -> 通过基类的指针来销毁派生类对象,基类的析构函数需要设置成虚函数。
总结:基类析构函数无法访问派生类的析构函数,便无法析构派生类;基类的构函数设置成虚函数后,便可以通过虚函数表找到到派生类析构函数的地址,从而完成派生类的析构。

这是因为通过基类指针来销毁派生类对象这个行为,当基类没有虚析构函数时会产生问题。我们知道删除指针对象是没有问题的,指针对象的析构函数会正确调用,但仅限于指针的类型所表示的对象大小。如果以一个基类指针指向其派生类,删除这个基类指针只能删除基类对象部分,而不能删除整个派生类对象,原因是通过基类指针无法访问派生类的析构函数
但是,如果像其它虚函数一样,基类的析构函数也是虚的,那么派生类的析构函数也必然是虚的,删除基类指针时,它就会通过虚函数表找到正确的派生类析构函数并调用它,从而正确析构整个派生类对象。

为什么构造函数不能是虚的?
原因是构造自己时,对象还不存在。虚函数需要有虚函数表,但这个表因为在构造阶段是不存在的,至少还没分配内存,无法实现定义要求。

TCP三次握手的过程?服务端建立连接过程?time_wait状态什么时候出现?为什么需要time_wait状态?

TCP与UDP的区别?

智能指针原理?共享指针怎么实现?

STL有哪些数据结构?各有什么特点?应用场合?
STL中list,vector,deque,map,set区别、联系和使用场景原理
1、如果你需要高效的随机存取(通过下标存取),而不在乎插入和删除的效率,使用vector;
2、如果你需要大量的插入和删除,而不关心随机存取,则应使用list;
3、如果你需要随机存取,而且关心两端数据的插入和删除,则应使用deque;
4、如果你要存储一个数据字典,并要求方便地根据key找value,那么map是较好的选择;
5、如果你要查找一个元素是否在某集合内存中(重复元素剔除),则使用set存储这个集合比较好。

malloc/free与new/delete的区别?

用什么编译器,怎么调试?
gdb编译器,CMake创建项目,debug模式下调试,IDE用的是VSCode。(gdb调试指令?多线程下怎么调试?)

电话二面

自我介绍

linux高性能服务器项目相关内容

  1. 服务端实现的过程,什么时候需要设置非阻塞? -> socket()、bind()、listen()、epoll_wait()、accept()、recv()、send()、线程池处理任务等,在aceept()后设置客户端的socket文件描述符为非阻塞。
  2. 线程池队列满了、空了如何解决?-> 两个条件变量(等待队列非空需要一个条件变量,等待队列非满需要一个条件变量),控制同一把互斥锁(保护任务队列同一个时刻只被一个线程操作)。

用过哪些开源框架?
高性能I/O框架库:libevent

迭代器(iterator)和指针(pointer)区别在哪?
如果只讨论 STL container 类的 iterator,它们其实都是一种泛型指针
迭代器实际上是对 “遍历容器” 这一操作进行了封装。
在编程中我们往往会用到各种各样的容器,但由于这些容器的底层实现各不相同,所以对他们进行遍历的方法也是不同的。例如,数组使用指针算数就可以遍历,但链表就要在不同节点直接进行跳转。

设计在循环中STL迭代器删除指定位置的元素?
STL 迭代器删除,插入元素发生的事情

  1. 对于序列式容器(vector、deque、list),删除当前的iterator会使后面所有元素的iterator都失效。这是因为vector,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。解决方法:使用erase方法后,返回的是下一个有效的iterator。
	/* 输出 1 2 4 5 6 */
	vector<int> val = {
    
     1,2,3,4,5,6 };  
	vector<int>::iterator iter;  
	for (iter = val.begin(); iter != val.end(); ){
    
      
	     if (3 == *iter){
    
    
	         iter = val.erase(iter);  //返回下一个有效的迭代器
	     } else {
    
    
	         ++ iter; 
	     } 

往某个位置插入一个元素,则这个位置之后的元素都要后移,因此后面的迭代器都会失效;如果引起内存的重新配置,所有的迭代器都将失效。解决方法:insert()返回当前被插入元素的有效迭代器。

    /* 输出 -1 0 -1 1 -1 2 3 4 5 6 */
    vector<int> val = {
    
     1,2,3,4,5,6 };
    vector<int>::iterator iter = val.begin();
    val.insert(iter, 0);
    for (iter = val.begin(); iter != val.end(); iter ++){
    
    
        if(*iter < 3){
    
    
            iter = val.insert(iter, -1);
            iter++; // 当前指向-1,地址加1后指向插入前iter指向的元素
        }
        
    }
    for(int a : val){
    
    
        cout << a << " ";
    }
    cout << endl;
  1. 对于关联容器(map,set),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前的iterator即可。这是因为map之类的容器,使用了红黑树来实现,删除一个结点不会对其他结点造成影响。
	/* 输出 1 2 4 5 6 */
    set<int> valset = {
    
     1,2,3,4,5,6 };
    set<int>::iterator iter;
    for (iter = valset.begin(); iter != valset.end(); iter++){
    
    
        if (3 == *iter){
    
    
            valset.erase(iter); //因为传给erase的是iter的一个副本,iter++是下一个有效的迭代器。
        } 
    }
    for(int a : valset){
    
    
        cout << a << " ";
    }
    cout << endl;

往iterator指向的位置插入一个元素,并不会对其他的节点造成影响,仅对插入的节点有影响,函数返回插入节点的迭代器。

    /* 输出  0 1 2 3 4 5 6 I10*/
	set<int> valset = {
    
     1,2,3,4,5,6 };
    set<int>::iterator iter = valset.begin();
    valset.insert(iter, 0);
    for (iter = valset.begin(); iter != valset.end(); iter++){
    
    
        if(*iter < 3){
    
    
            valset.insert(iter, 10); // set中插入的节点会排序,且不能重复插入
            cout << *iter << endl;
        }
    }
    for(int a : valset){
    
    
        cout << a << " ";
    }
    cout << endl;

用过的性能测试工具? -> linux高性能服务器编程第16章:服务器调制、调试和测试
webbench

用过Linux吗?举一些指令

LInux用过哪些性能分析工具?
开发人员调试和测评服务器程序的工具)-> linux高性能服务器编程第17章:系统监测工具
tcpdump、nc、strace 、lsof 、netstat 、vmstat 、ifstat 、mpstst …

超全整理!Linux性能分析工具汇总合集

纯虚函数作用

智能指针知道哪些?随便说一个实现的原理


猜你喜欢

转载自blog.csdn.net/XindaBlack/article/details/106575085