字节跳动提前批后端开发工程师一面

本次面试内容主要包括C++基础知识、操作系统、计算机网络、数据结构与算法。

1. 了解多态吗?多态的实现过程是怎样的?

多态即“一个接口,多种实现”,不同对象接收到相同消息产生不同行为的现象。
多态的作用是接口重用。

多态分为编译时多态和运行时多态,也称为静态多态和动态多态。静态多态通过函数重载和模版来实现,动态多态通过重写和虚函数实现。
静态多态是在编译时就确定调用函数的类型,将函数实现和函数调用关联起来。因此,用基类指针指向子类或基类对象引用子类对象时,调用的仍然是基类的函数方法。
动态多态可以避免此情况出现,在运行的时候才确定调用的是哪个函数,动态绑定。因此,用基类指针指向子类或基类对象引用子类对象时,调用的是子类的函数方法。

动态多态需要虚函数进行实现。虚函数使用virtual关键字修饰,且子类需要重新实现该函数。
虚函数由虚函数表和虚函数指针组成。虚表是一个指针数组,其元素是虚函数指针。虚表属于一个类,一个类只需要一个虚表,同一类下的所有对象都使用同一虚表。
若子类未重写基类都虚函数,则基类和子类虚表地址相同。

2. 我们来看一段代码,其对应输出是什么?

#include <iostream>
using namespace std;

class A
{
public:
    void fun(){cout<<"A:fun()"<<endl;}
};

class B:public A
{
public:
    void fun(){cout<<"B:fun()"<<endl;}
};

int main()
{
    A a;
    B b;
    A *pa1 = &a;
    B *pb1 = &b;
    A *pa2 = NULL;
    B *pb2 = NULL;
    A *pa3 = &b;
    pa1->fun();//...1
    pb1->fun();//...2
    pa2->fun();//...3
    pb2->fun();//...4
    pa3->fun();//...5
    return 0;
}

此处考察多态及虚函数基本原理,对应输出为:

A:fun()
B:fun()
A:fun()
B:fun()
A:fun()

第五项输出为A:fun()是因为调用了基类方法。虽然使用的是基类指针指向子类地址,但由于基类方法未用virtual关键字修饰,实际采用的是静态联编,即在编译时期就将函数的实现与调用关联起来,相应的基类指针在编译时期未指向子类,所以调用的依然是基类的方法。

若基类方法使用virtual关键字修饰,则输出为:

A:fun()
B:fun()
报错
报错
B:fun()

三四项报错是因为程序采用动态联编,运行期间基类子类指针指向NULL,而虚函数由虚表和虚指针构成,指针指向NULL便无法访问虚表。

3. 操作系统了解吗?进程和线程是什么?说一下他们的区别

进程是正在运行的程序的实例,是资源分配的最小单位。
线程是进程中的一个实体,是CPU调度和分派的最小单位。

  1. 线程在进程下行进,一个进程可以包含多个线程。
  2. 进程拥有独立的堆栈空间和数据段,系统开销比较大;线程拥有独立的堆栈空间,但是共享数据段,开销比较小,上下文切换速度也比进程快,效率高。
  3. 进程有独立的地址空间,一个进程崩溃后不会对其它进程产生影响;而线程只是一个进程中的不同执行路径,一个线程挂掉将导致整个进程挂掉。
  4. 进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。
  5. 一个CPU同一时间只能处理一个线程,但可以通过时间分片来实现多任务的并发。线程使CPU系统更加有效,因为操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上,达成真正意义的并行。

4. 进程之间可以相互访问吗?线程呢?

进程间通信可以使用管道,信号,消息队列,共享内存,套接字等通信机制,而线程间可以直接共享数据段。

5. 说一下TCP三次握手和四次挥手

三次握手


TCP建立链接的三次握手

  1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
  2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
  3. 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手。

四次挥手


TCP断开连接的四次挥手

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1, Server进入CLOSED状态,完成四次挥手[1]。

6. 能不能只进行两次握手?

可简单概括为:防止已失效的连接请求又传送到服务器端,因而产生错误。
为了实现可靠数据传输, TCP 协议的通信双方都必须维护一个序列号,以标识发送出去的数据包中哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认[2]。

7. 四次挥手中的TIME_WAIT的作用是什么?

  1. 可靠地实现TCP全双工连接的终止
    在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,
    因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
    因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。
  2. 允许老的重复分节在网络中消逝
    TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。
    在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。
    为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值