修炼之道 --- 其二
序言
在这篇文章中的内容,我们主要关注 C++ 和计算机网络 方面,在今天的文章中可有一个重量级嘉宾 虚函数。在回答问题的同时,引发了一些我的疑问。有些超出我能力的问题我可能不会解释那么好,欢迎大家指正!
话不多说,承接上文,开始吧!
一、C++
1. 介绍一下虚函数,虚函数怎么实现的
虚函数是实现多态的关键。当我们的类声明一个函数为虚函数,并且派生类重写了该方法时就能在调用时实现动态绑定(根据调用接口对象的类型来决定行为)的效果。
虚函数的实现依赖于虚函数表。当一个类中包含一个虚函数时,内部会创建一个虚函数表,虚函数表存储的是虚函数的地址。在继承时,虚函数表也会继承给派生类。当派生类重写了虚函数时,会将新的地址覆盖掉虚函数表中原来对应虚函数的地址。这样在我们调用时,就能够实现动态绑定的效果。
问题拓展:我一直疑问怎么确定虚函数对应虚函数表的哪一个位置呢?
答:虚函数按照类中声明的顺序被插入虚函数表。基类的虚函数首先进入虚函数表,然后是派生类的覆盖或新增的虚函数。
2. 多态和继承在什么情况下使用
当多个类存在公共的属性或方法时,可以将这些共性提取到一个基类中,通过继承减少代码冗余。
3. C++ 从源程序到可执行程序的过程
从源程序到可执行程序分为四个步骤,分别是 预编译,编译,汇编,链接。
在预编译过程中:
- 替换宏定义(#define)
- 展开头文件(#include)
- 条件编译(#if、#ifdef、#ifndef)
- 去除注释信息
在编译过程中:
- 语法和语义检查(查看是否存在语法错误,类型错误)
- 生成中间代码: 将源代码转换为中间表示(IR)
- 优化: 对中间代码进行优化,例如删除冗余代码或内联函数。
- 生成汇编代码: 将优化后的中间代码转换为汇编语言代码
在汇编过程中主要是将我们的汇编代码翻译为二进制代码保存在文件中。
在链接的过程中,将多个 .o 文件合并为一个 .exe 可执行文件:
- 符号解析:我们经常将一个类声明定义分离,当我们需要使用一个类的时候只需要包含他的头文件,但是这个类的具体实现是在 .cpp 文件中。现在我们就需要将使用的变量方法和在 .cpp 中的进行匹配。
- 库文件链接:我们编写代码时基本离不开库文件的使用,所以我们需要将库文件也加入到可执行文件中(静态链接,动态链接)。
- 生成可执行文件
4. 右值引用、移动语义、完美转发
右值引用允许我们绑定一个右值,延长该值的生命周期。右值引用可用于移动语义和完美转发提升效率。
常见的右值包括:字面量,临时变量
移动语义:采取转移资源而非复制的方式来构造对象。
完美转发:避免在参数传递的过程中丢失参数的属性,退化为左值引用。完美转发会保持该参数的属性,按照实际的类型转发,避免未定义行为。
注:这个内容还是蛮重要的博主有一篇文章专门讨论过,如若不嫌弃,请移步到链接观看:点我点我
二、计算机网络
说到计网就肯定离不开三次握手四次挥手,让我们来吧!
1. 三次握手四次挥手的过程
首先是三次握手的过程:
- 客户端发送 SYN 进入 SYN_SENT 状态
- 服务端接收到之后进入 SYN_RCVD 状态,同时发送 SYN + ACK
- 客户端接收到之后进入 ESTABLISHED 状态,发送 ACK(可携带数据)
- 服务端接收到之后进入 ESTABLISHED 状态
四次挥手的过程:
- 客户端发送 FIN 进入 FIN_WAIT_1 状态
- 服务端接受到之后进入到 CLOSE_WAIT 状态,发送 ACK
- 客户端接收到之后进入到 FIN_WAIT_2 状态
- 服务端发送 FIN 进入到 LAST_ACK 状态
- 客户端接收到之后进入到 TIME_WAIT 状态,发送 ACK
- 服务端接受到之后进入到 CLOSE 状态
- 2MSL时间之后,进入 CLOSE 状态
2. 为什么是三次握手,四次挥手
在三次握手中,首先是避免历史连接,如果有一个历史连接先到达服务端中间那次握手起到了确认连接缓冲的作用,避免直接建立连接(中间状态来二次确认);确认序列号,每次连接初始的序列号都是不同的(避免历史数据造成干扰);以最小的成本来确认双方具有接受和发送数据的环境
在四次握手中,前两次是主动的一方断开连接,后两次是被动的一方断开连接,且都保证对方能够收到请求。但是某些情况下是可以三次挥手的(当断开请求接收时,如果被动的一方没什么要说的了就可以以捎带应答的方式发送 SYN + ACK)
3. TCP 断开时为什么需要 2MSL
保证被动断开的一方能够正确的断开连接。被动的一方只有接收到了最后一个 ACK 才能正确断开连接;丢弃本次连接的阻塞数据,避免在本次连接中的数据干扰下一次的连接传输。
4. 粘包拆包是什么,发生在哪一层
TCP 协议是面向字节流的可能在一次发送中将多个数据合并为一个数据包,或者是一个数据拆分为多个数据包,数据之间没有间隔,这是粘包。我们将数据按照实际发送的格式进行拆分,这是拆包。发生在以 TCP 为协议的应用层。
解决方案:
- 消息定长:不灵活,但简单
- 自定义用户层协议:灵活,比如 HTTP
总结
今天的内容就到这里吧,保持进步,保持学习!