[杂学笔记]工厂模式、多态、内存空间区域划分、cp指令破坏软连接问题、UDP如何实现可靠传输、滑动窗口的原理、进程与线程、线程之间的通信
目录
1.工厂模式
2.多态
3.内存空间区域划分
4.cp指令破坏软连接问题
5.UDP实现可靠传输
6.滑动窗口的原理
7.进程与线程
8.线程之间的通信
1.工厂模式
工厂模式是一种创建对象的设计模式。它提供了一种创建对象的方式,将对象的创建和使用分离,通过一个工厂类来负责创建对象。这样可以将对象创建的逻辑封装在工厂类中,而不是在使用对象的代码中直接通过new操作符来创建对象。
优势:工厂类接口会直接返回对象,不需要关心对象如何创建的。那么在创建对象的时候,如果想改变对象的创建方式,直接在工厂类代码中改变即可,不需要再依次的修改创建对象的new代码了。配合多态体系的话,还可以降低mian函数与具体产品类的耦合度。例如:有A、B两个类,再创建的时候就需要A* a = new A(); B* b = new B();而使用多态和工厂模式的话就直接这样就好了 Product* a = CarFactory::createCar("A");Product* b = CarFactory::createCar("B");产品再多的话,也是使用同一个父类,调用同一个工厂类函数。
缺点:如果产品种类过多的话,会导致工厂的代码变得复杂,难以维护。
#include <iostream>
// 汽车抽象类
class Car {
public:
virtual void drive() = 0;
};
// 轿车类
class Sedan : public Car {
public:
void drive() override {
std::cout << "Driving a sedan." << std::endl;
}
};
// SUV类
class SUV : public Car {
public:
void drive() override {
std::cout << "Driving an SUV." << std::endl;
}
};
// 汽车工厂类
class CarFactory {
public:
static Car* createCar(std::string carType) {
if (carType == "sedan") {
return new Sedan();
} else if (carType == "suv") {
return new SUV();
}
return nullptr;
}
};
int main() {
// 通过工厂类创建轿车对象
Car* sedan = CarFactory::createCar("sedan");
if (sedan) {
sedan->drive();
delete sedan;
}
// 通过工厂类创建SUV对象
Car* suv = CarFactory::createCar("suv");
if (suv) {
suv->drive();
delete suv;
}
return 0;
}
2.多态
- 静态多态:也称为编译时多态,体现在函数重载和运算符重载方面,在编译阶段就已经去确定了函数调用的具体版本了。他底层会对同名函数,根据函数的参数列表的个数、顺序以及类型生成新的函数名称,就可以确定调用哪个函数了。静态多态的执行效率高。
- 多态多态:也称为运行时多态,体现在继承和虚函数体系下。在父类中声明虚函数,子类重写虚函数。当通过父类的指针或者引用调用虚函数的时候,程序会在运行的时候根据指针或者引用所指向的实际对象的类型来决定调用哪一个版本的虚函数。但是在底层实际上是,在创建子类的时候,子类重写的虚函数会覆盖虚函数表中的地址,那么父类指针指向子类对象的时候,访问虚函数表地址访问的就是子类覆盖后的地址,实现了指向子类的话就会调用子类的函数。
3.内存空间区域划分
从低到高分别为:代码区、字符常量区、静态存储区(已初始化全局数据区、未初始化全局数据区)、堆区、共享区、栈区、命令行参数与环境变量区、内核空间区域。其中共享区会存放一些共享内存、消息队列、动态库等等的映射。
4.cp指令破坏软连接问题
cp默认复制一个软连接的时候,会对软连接进行解引用,来访问到软连接指向的文件,从而拷贝文件的内容,拷贝后的内容是一个普通文件,而不是拷贝一个软连接。
那么如何拷贝软连接呢?使用-a、-d或者-P选项,在遇到软连接的时候,会直接复制符号连接本身。
5.UDP实现可靠传输
因为UDP是传输层属于系统层面,我们无法去更改传输层的执行操作,只能在用户层进行编程来实现UDP的可靠传输。通过增加用户层自定义协议的报头字段,把UDP中没有的报头字段在应用层添加就好了。
- 确认应答机制与超时重传机制:可以在发送的数据报内部带有TCP类似的确认序号字段。发送方在发送数据之后设置定时器,并保存发送的数据,如果一段时间内没有收到确认序号的话,就重新发送该数据报
- 32位序号:在用户层中定义一个类似于TCP的接收缓冲区,将接收的数据放到缓冲区中,根据报头的序号进行排序,排序之后就知道有没有丢包了,之后再进行数据的处理。
6.滑动窗口的原理
TCP的发送缓冲区内部被划分为了三个区域,分别是已发送已确认区域,已发送未确认区域以及待发送区域。当发送方接收到了ACK应答报文,会根据确认序号移动指针来改变区域的划分。如果说丢包了,会收到连续相同序号的ACK应答报文,会将待发送区域的指针移动到ACK报文的确认序号位置,重新发送数据。如果说没有任何问题的话,就会将确认的数据划分到已发送已确认的区域了。
通过滑动窗口的区域划分实现了将没有收到确认序号的报文保存起来的作用,当收到后也不需要释放这部分数据,而是等待其他数据到来将其覆盖即可。同时实现了流量控制,接收方可以通过调整窗口的大小来控制发送方发送数据的速率。
7.进程与线程
进程是CPU执行调度的基本单位,每个进程都拥有自己独立的地址空间,不同进程之间是无法直接进行互相访问数据资源的。线程是进程内部的一个执行流。一个进程可以包括多个线程,这些线程共享一个进程地址空间,那么也使得线程之间可以执行进行数据的相互访问操作。
进程的创建开销相对来说较大,每一个线程都需要创建内存空间、进行页表映射、创建文件描述符表等等操作。而线程只需要创建一个PCB结构就可以,内部的很多内容都是共享一个进程的,所以不需要单独创建了。
通信上来看,进行之间的通信相对复杂一下,管道需要创建文件、消息队列和共享内存需要申请内存空间并建立页表映射、信号量也不例外。进程间的通信首先就是需要让进程之间都能访问到同一个资源,那么必定会麻烦一些。而线程是直接共享进程内的变量,没有通信会方便一些,那么同时也缺乏独立性,一个线程崩溃了会导致整个进程奔溃。
8.线程之间的通信
- 共享内存(变量):线程是共享进程的地址空间的,所以可以随意访问进程内的变量。
- 条件变量:通常和互斥锁一起使用,用于实现线程之间的同步。一个线程会等待某一个条件的就绪,另一个线程会再条件满足的时候通知等待的线程。那么这个线程就可以继续向下执行了。
- 信号量:本质上是一个计数器,通常也是配合锁用于实现线程之间的同步。在线程访问资源之前需要先申请信号量,如果没有的话就需要等待了。