- 博客(102)
- 收藏
- 关注
原创 什么是单例模式?
由于使用者在类外部不能使用构造函数,所以在类内部创建的这个唯一的对象必须是静态的,这样就可以通过类型来访问了,为了不破坏类的封装,我们都会把这个静态对象的访问权限设置为私有的。在一个项目中,全局范围内,某个类的实力有且仅有一个,通过这个唯一实例想其他模块提供数据的全局访问,这种模式就叫单例模式。饿汉模式下是没有线程安全问题的,因为在这种模式下访问单例对象的时候,这个对象已经被创建出来了。懒汉模式是在类加载的时候不去创建这个唯一的实例,而是需要使用的时候再进行实例化。拷贝赋值操作符重载函数私有化或者禁用。
2024-09-23 09:41:47
714
原创 设计模式三原则
在实际项目中的对不同的相机进行了开发,在其中包含了海康相机和basler相机,根据上面说到的依赖倒置原则,其中高层模块就是我们写的应用程序,低层模块可以看成是相机的api函数,我们在开发的时候不能直接让应用程序直接调用这些api函数,要是直接调用的话客户要跟换相机从海康相机换成basler相机的话,这样我们就需要把所有海康的api接口换成basler的api接口,这样的工作量很大。而我们的高层只需要调用这些接口层的函数,不需要关注低层模块是如何实现的,这让整个程序维护起来就十分的方便!
2024-09-22 15:38:58
608
原创 TCP四大拥塞控制算法总结
这个机制就不需要等到重传定时器超时,所以叫快速重传,而快速重传后没有使用慢启动算法,而是拥塞避免算法,所以这又叫做快速恢复算法。如果收到了新的ACK,表明重传的包成功了,那么退出快速恢复算法。还有一个ssthresh,是一个上限,当cwnd >= ssthresh时,会进入“拥塞避免算法”。过了慢启动阈值后,拥塞避免算法可以避免窗口增长过快导致窗口拥塞,而是缓慢的增加调整到网络的最佳值。四大算法:1.慢启动,2.拥塞避免,3.拥塞发生,4.快速恢复。每当收到一个ACK,cwnd大小加一,呈线性上升。
2024-09-21 20:51:05
1791
原创 数字签名和CA数字证书的核心原理
看了蛋老师的视频就很容易理解了,首先对服务器的公钥和信息进行哈希运算得到一个短字符串,然后用CA机构中的私钥对这一短字符串进行加密就得到了一个数字签名,然后就这个数字签名放到数字证书中,同时服务器的公钥也放在数字证书中。这样就可以把这个数字证书发给浏览器。浏览器收到数字证书之后会使用CA机构中的公钥对数字签名进行解密得到一个短字符串,同时再对数字证书中的信息进行哈希运算也得到一个短字符串,这样两者一对比就能知道是否被篡改了。没有被篡改的话就取出公钥就能接下来的操作生成会话密钥了。
2024-09-20 22:51:00
496
原创 同步 异步 阻塞 非阻塞
非阻塞:非阻塞操作允许调用者在操作尚未完成时继续执行其他任务。举例:在非阻塞式IO操作中,程序可以继续执行其他任务,即使数据还没有从磁盘读取完成。异步:在异步操作中,调用者可以在操作执行期间继续执行其他代码,而不用等待操作完成。举例:在一个异步网络请求的时候,客户端发送请求后立刻继续执行其他代码,当服务器响应时,通过回调函数处理响应数据。阻塞:阻塞操作会使调用者在操作完成之前无法继续执行其他操作。调用者被挂起,直到操作完成。举例:在阻塞IO操作中,如果程序在等待数据从磁盘中读取完成之前,无法进行其他操作。
2024-09-12 20:25:03
352
原创 虚函数表和虚函数表指针
让虚函数表指针指向虚函数表,我们要知道的是,虚函数表指针在每个对象中是不一样的,但是它们指向的虚函数表是一样的。虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期就可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不在堆中。虚函数表类似一个数组,类对象中存储vptr指针指向虚函数表,即虚函数表不是函数,不是程序代码,不可能存储在代码段。2. 虚函数表指针vptr的初始化时间?1.基类的虚函数表存放在内存的什么区?
2024-09-09 16:57:13
312
原创 共享内存和信号量
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。shmget——创建/打开共享内存shmat——将共享内存与当前进程相关联shmdt——将当前进程与共享内存间脱离关联shmctl——操控共享内存因为是共享一块内存,所以其中必然涉及到同步的问题。共享内存如何实现进程间的同步呢?也就是说不同的进程不能同时访问同一块内存》这个时候就需要使用到信号量。
2024-09-08 10:59:58
441
原创 rtsp服务器逻辑
上面我们提到cbTimeout回调函数,里面调用了handleTimeout()函数,然后由子类去实现发送逻辑this->sendFrame(frame),在子类中真正的发送逻辑还是在父类中实现的也就是sendRtpPacket(RtpPacket* packet)函数,在这个函数里还执行了一个回调函数,最终发送者是在MediaSession中,在MediaSession中调用了handleSendRtpPacket()函数才将数据最终发送出去。在addSink函数中就设置了sink中的回调函数。
2024-09-03 15:21:13
1400
1
原创 引用传递和值传递
引用是变量的别名:在引用传递中,函数内的参数实际上是调用者传入的变量的别名,他们共享同一块内存空间。引用传递:在引用传递的情况下,函数得到的是实际参数的引用,这意味着函数内部对参数的操作会直接作用于原始变量。1.值传递:传递的是参数的副本,函数内部对参数的操作不会影响原始变量。值传递:函数得到的是实际参数的一个拷贝,修改这个拷贝不会影响原始的变量。2.指针传递:传递的是参数的地址,函数可以通过指针修改原始变量的值。3.引用传递:传递的是参数的引用,函数操作的是原始变量本身。为什么引用传递会改变传入的值?
2024-08-30 09:51:02
245
原创 右值引用?
在上面的代码中给Test类中添加了移动构造函数(参数为右值引用类型),这样在进行Test t = getObj()操作的时候并没有调用拷贝构造函数进行深拷贝,而是调用了移动构造函数,这个函数中只是进行了浅拷贝,没有对临时对象进行深拷贝,提高了性能。右值引用具有移动语义,移动语义可以将资源(堆,系统对象等)通过浅拷贝的方式从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建,拷贝以及销毁,可以大幅提高c++应用程序的性能。C++11中的右值分为两种:一种是纯右值,一种是将亡值。
2024-08-29 20:32:52
246
原创 虚函数表和虚函数表指针
1.虚函数表的创建时机:由编译器编译的时候生成,virtual关键字修饰的函数。a. 调用基类的构造函数,把A的虚函数表的地址赋值给vptr。b. 调用子类的构造函数,把B的虚函数表的地址赋值给vptr。类对象构造的时候,把类的虚函数表地址赋值给虚函数表指针。类创建了不同的对象,对象中的虚函数表指针是不一样的,虚函数表在编译的时候就存在了,存放在.rodata中或代码区。虚函数表指针就是指向虚函数表的指针。继承的情况下虚函数表的继承过程。每个类只有一个虚函数表。虚函数表和虚函数表指针的创建时机。
2024-08-29 11:20:03
417
原创 死锁是什么?
2.请求和保持:当一个进程持有了某个资源之后接着请求另一个资源,但是另一个资源被其他进程所占用,此时请求阻塞,但是不会释放自己的资源。1.互斥:一个进程持有了一个资源而不允许其他进程访问,需要等待该进程释放资源才可访问。3.不可剥夺条件:进程已获得的资源只能自己主动释放不能被其他进程抢占。什么是死锁:是指多个进程因为争夺资源而造成多个进程相互等待的现象。4.环路等待条件:若干进程形成一个首尾相连循环等待资源的关系。
2024-08-27 20:48:29
188
原创 malloc的底层实现原理?
3.malloc小于128k的内存,使用brk分配内存,将堆顶指针往高地址推,malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配。当最高地址空间的空闲内存超过128k时,执行内存紧缩操作。2.brk将堆顶指针向搞地质移动,获得新的内存空间,mmap是在进程的虚拟空间中(栈区和堆区之间的共享数据区)找一快空闲的虚拟内存。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆节点,然后就将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。
2024-08-26 19:58:36
517
原创 new和malloc的区别
4.new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无需进行类型的转换,故new是符合类型安全性的操作符。而malloc内存分配成功后返回的是void*类型的指针,需要通过强制转换的方式将void*指针转换成我们需要的类型。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。3.new操作符申请内存分配时无需指定内存块的大小,编译器会根据类型信息自动计算。2.new后面跟着的是类名,malloc函数里面是要申请分配内存空间的大小。
2024-08-26 19:14:41
305
原创 static的作用
函数体内static变量的作用范围就是该函数体,该变量的内存只被分配一次,因此其值在下次调用的时候仍然维持上一次的值。在类中的static成员函数属于这个类所有,这个函数不接收this指针,因而只能访问类的static成员变量。在模块内的static函数只可被这一模块内的其他函数调用,这个函数的使用那范围被限制在申明它的模块内。在模块内的static全局变量可以被模块内所有函数访问,但是不能被模块外的其他函数访问。static类成员函数不能访问非static的类成员,只能访问static修饰的类成员。
2024-08-26 19:07:46
288
原创 TCP和UDP的区别?
3.可靠性:tcp是可靠传输:有以下几个方面来保证数据的可靠传输:a.序列号:tcp报文包含序列号,确保完整接收,丢失重复数据,排序;而udp是不可靠传输:不保证消息交付,不保证交付顺序,不进行拥塞控制,不进行流量控制。2.数据的传输方式:tcp是基于字节流传输,由于MSS和MTU的原因,完整的用户消息可能被拆分为多个tcp报文进行传输,由于在发送方有发送缓冲区,所以多个数据包可能会粘包后进行传输,在接收缓冲区就需要处理粘包问题;而udp是基于报文传输的,udp每次发都是完整的报文。
2024-08-24 16:14:31
296
原创 进程和线程的区别
进程的切换效率低,线程的切换效率高。这涉及到上下文切换的问题,在进程切换的时候涉及到现场运行环境有:cpu寄存器,程序计数器,还有用户空间信息和内核空间信息pcb等;但是线程切换的时候涉及到的现场环境有:cpu寄存器,程序计数器等;需要保存和恢复的信息比进程的少,所以线程的切换效率高。4.从健壮性上看:进程的健壮性更高一些,因为如果一个进程挂了,其他的进程不会收到影响;但是如果一个线程挂了,那么进程也就挂了,对应其他线程也挂了。3.从所属关系上看:线程属于进程,而进程包含多个线程。
2024-08-24 11:31:53
280
原创 http的keepalive和tcp的keepalive
链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有发送数据,链接还需不需要保持,这种情况下tcp协议中给出了一个方法:当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了很多次之后则认为链接丢失,没有必要继续保持链接。而http的keepalive说的是如何避免进行重复的tcp三次握手和四次挥手。
2024-08-22 15:21:22
515
原创 HTTPS的加密过程
首先客户端先向服务器发了自己的TLS版本号,自己支持的加密套件和一个随机值1。服务端收到后保存随机值1,然后也生成一个随机值2发给客户端,同时发送的还有证书和公钥。此时客户端收到之后也保存随机值2,同时生成一个预主密钥(也是一个随机值)通过得到的公钥进行加密传给服务端,服务端收到之后使用私钥进行解密,得到预主密钥。此时将客户端和服务端的随机数1,随机数2,预主密钥相加得到一个会话密钥,然后双方就根据这个会话密钥进行对称加密。也就是通过SSL/TLS进行加密的。
2024-08-22 15:09:18
363
原创 Session知识总结
简单点说就是:session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。session可以存储在服务器上的文件,数据库或者内存中。除了可以将用户信息通过Cookie存储在用户浏览器,也可以利用session存储在服务器端,存储在服务器端的信息更加安全。2.服务器验证该用户名和密码,如果正确则把用户信息存储到Redis中,它在Redis中的key成为session ID,
2024-08-22 15:03:11
160
原创 依赖倒置原则
在实际项目中的对不同的相机进行了开发,在其中包含了海康相机和basler相机,根据上面说到的依赖倒置原则,其中高层模块就是我们写的应用程序,低层模块可以看成是相机的api函数,我们在开发的时候不能直接让应用程序直接调用这些api函数,要是直接调用的话客户要跟换相机从海康相机换成basler相机的话,这样我们就需要把所有海康的api接口换成basler的api接口,这样的工作量很大。在我的项目中应用到了设计模式中的一个设计原则,依赖倒置原则:就是高层模块不直接依赖低层模块,两者都应该依赖抽象。
2024-08-22 14:55:38
207
原创 HTTP无状态是什么意思?Cookie又是什么?
Cookie的出现就是因为http是无状态的协议,换句话说就是服务器记不住你,每个你刷新一个浏览器,就需要重新输入一次账号密码进行登录,这显然是让人无法接收的,Cookie的作用就好比服务器给你贴了一个标签,然后每次你再向服务器发请求的时候,服务器就能够根据Cookie认出你。http无状态意味着每个http请求都是独立的,服务器不会自动保留上一次请求的上下文或状态。http是无状态的,主要是为了让http协议尽可能的简单,使得它能处理大量事物,而http1.1引入了Cookie来保存状态信息。
2024-08-22 14:55:28
461
原创 代码随想录训练营第五十八天
拓扑排序指的是一种 解决问题的大体思路, 而具体算法,可能是广搜也可能是深搜。大家可能发现 各式各样的解法,纠结哪个是拓扑排序?其实只要能在把 有向无环图 进行线性排序 的算法 都可以叫做 拓扑排序。实现拓扑排序的算法有两种:卡恩算法(BFS)和DFS卡恩1962年提出这种解决拓扑排序的思路一般来说我们只需要掌握 BFS (广度优先搜索)就可以了,清晰易懂,如果还想多了解一些,可以再去学一下 DFS 的思路,但 DFS 不是本篇重点。接下来我们来讲解BFS的实现思路。
2024-08-11 16:43:32
589
原创 代码随想录训练营第五十六天
节点3 的入度为 2,但在删除边的时候,只能删 这条边(节点1 -> 节点3),如果删这条边(节点4 -> 节点3),那么删后本图也不是有向树了(因为找不到根节点)。本题的本质是 :有一个有向图,是由一颗有向树 + 一条有向边组成的 (所以此时这个图就不能称之为有向树),现在让我们找到那条边 把这条边删了,让这个图恢复为有向树。我们来想一下 有向树的性质,如果是有向树的话,只有根节点入度为0,其他节点入度都为1(因为该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点)。
2024-08-11 16:10:24
522
原创 代码随想录训练营第五十五天
为什么说这道题目是并查集基础题目,题目中各个点是双向图链接,那么判断 一个顶点到另一个顶点有没有有效路径其实就是看这两个顶点是否在同一个集合里。最后 isSame(int u, int v) 判断是否是同一个根 就可以了。使用 join(int u, int v)将每条边加入到并查集。如何算是同一个集合呢,有边连在一起,就算是一个集合。简单介绍并查集之后,我们再来看一下这道题目。以上模板中,只要修改 n 大小就可以。此时我们就可以直接套用并查集模板。并查集可以解决什么问题呢?第一题:寻找存在的路径。
2024-08-11 15:55:35
233
原创 代码随想录训练营第五十四天
如果我们是处理当前访问的节点,当前访问的节点如果是 true ,说明是访问过的节点,那就终止本层递归,如果不是true,我们就把它赋值为true,因为这是我们处理本层递归的节点。首先明确,本题中什么叫做处理,就是 visited数组来记录访问过的节点,该节点默认 数组里元素都是false,把元素标记为true就是处理 本节点了。此时就要在思考本题的要求了,本题是需要判断 1节点 是否能到所有节点,那么我们就没有必要回溯去撤销操作了,只要遍历过的节点一律都标记上。
2024-08-11 15:37:39
1071
原创 代码随想录训练营第四十九天 42接雨水 84柱状图中的最大矩形
第一题:思路:单调栈就是保持栈内元素有序。和一样,需要我们自己维持顺序,没有现成的容器可以用。通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。而接雨水这道题目,我们正需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积。
2024-08-11 15:19:30
959
原创 代码随想录训练营第四十八天 739每日温度 496下一个更大元素I 503下一个更大元素II
加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于!我们要保持一个递增单调栈(从栈头到栈底),所以将T[0]弹出,T[1]加入,此时result数组可以记录了,result[0] = 1,即T[0]右面第一个比T[0]大的元素是T[1]。加入T[1] = 74,因为T[1] > T[0](当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况)。
2024-08-11 12:08:02
726
原创 代码随想录训练营第四十五天 115不同的子序列 583两个字符串的删除操作 72编辑距离
第一题:思路:1.确定dp数组(dp table)以及下标的含义dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。为什么i-1,j-1 这么定义我在中做了详细的讲解。2.确定递推公式这一类问题,基本是要分析两种情况当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。
2024-08-11 10:13:58
1001
原创 代码随想录训练营第四十四天 1143最长公共子序列 1035不相交的线 53最大子数组和 392判断子序列
t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];if (s[i - 1] == t[j - 1]),那么dp[i][j] = dp[i - 1][j - 1] + 1;从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1],所以dp[0][0]和dp[i][0]是一定要初始化的。
2024-08-11 09:50:45
728
原创 代码随想录训练营第四十三天 300最长递增子序列 674最长连续递增序列
为什么一定表示 “以nums[i]结尾的最长递增子序” ,因为我们在做递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小,那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾, 要不然这个比较就没有意义了,不是尾部元素的比较那么 如何算递增呢。但dp[i][0] 和dp[0][j]要初始值,因为 为了方便递归公式dp[i][j] = dp[i - 1][j - 1] + 1;dp[i] 是有0到i-1各个位置的最长递增子序列 推导而来,那么遍历i一定是从前向后遍历。
2024-08-10 22:09:57
784
原创 代码随想录训练营四十二天 188买卖股票的最佳时机IV 309买卖股票的最佳时机含冷冻期 714买卖股票的最佳时机含手续费
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);一定是选最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);那么dp[i][1]究竟选 dp[i-1][0] - prices[i],还是dp[i - 1][1]呢?
2024-08-10 10:57:07
591
原创 代码随想录训练营第三十八天 322零钱兑换
凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false了。dp[j] 可以由dp[j - i * i]推出, dp[j - i * i] + 1 便可以凑成dp[j]。
2024-08-10 09:50:49
794
原创 代码随想录训练营第五十二天 孤岛的总面积
将所有在边界的岛屿所在的visited数组位置都置为true,剩下的visited[i][j] == true && grid[i][j] == 1的位置就是孤岛,将其置为1即可。第二部:再遍历地图,遍历0的方格,并统计该1(由0变成1)周围岛屿面积,将其相邻面积相加在一起,遍历所有0之后,就可以得出选一个0变成1之后的最大面积。第一步:先遍历一遍地图,得出各个岛屿的面积,并做编号记录。同样从第二组边界的边上节点逆流而上,将遍历过的节点也标记上。从第一组边界上的节点逆流而上,将遍历过的节点都标记上,
2024-08-05 21:07:24
300
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅