- 博客(91)
- 收藏
- 关注
原创 C++多态机制详解(多态实现原理,单继承和多继承时虚函数表,菱形继承时的虚函数表原理)
多态就是多种形态。就是不同对象在完成同一种行为的时候结果不同。一种情况是不同的对象,调用同一个函数,但是出现了不同的结果。1.静态多态:函数重载(看起来调用的是一个函数但是传递不同的参数有不同的行为,比如cout的自动识别类型)2.动态多态:用一个基类对象的引用或者指针去调用重写完成的虚函数,更具引用或指向的对象不同,出现不同的行为静态:是指在编译阶段实现动态:是指在程序运行阶段实现被virtual修饰的函数就是虚函数public:这里的func1就是虚函数。
2024-03-23 17:56:04
768
原创 C++继承机制详解(单继承,多继承,菱形继承,虚继承)
继承的核心是代码的复用,继承机制就是面向对象语言对于类保持原来的结构,并对其进行扩展的重要手段。在继承里有这样两个类,基类(父类),派生类(子类)。继承就是子类继承父类,子类的结构相当于是在父类的结构上面的一个扩展。继承是类在设计层次的复用。继承可以说是C++语法复杂的一个方面,因为有了多继承,所以就会存在菱形继承,为了解决菱形继承又有了菱形虚拟继承。不仅底层实现复杂,对性能也有影响。所以一般不能设计出菱形继承。大多数情况都是使用单继承。像是java只有单继承。
2024-03-22 10:42:28
941
原创 模板高级使用(非类型模板参数,特化,分离编译)
如果基础的模板参数带有const,比如上面代码通用模板的类型时const T&,我们特化出来的类型目的是将T替换,但是这时候特化后的T应该写在cosnt &的前面不然就会报错。使用模板可以让编译器替我们完成一些重复的工作,同时也可忽略类型,让我们只关注代码逻辑的本身,但是有些特殊情况模板的默认处理逻辑并不能完成我们的需要,这时候就有了模板的特化。如上这里的第二个模板参数就是一个常量,但是非类型模板参数只能是int类型的常量,其他比如:double,字符串或者自定义类型变量那么都是不行的。
2024-03-21 22:44:11
707
原创 STL_list文档使用介绍与底层代码实现简介
在STL中list底层使用的是双向带头循环链表,这是一个很完善的结构,可以做到在O(1)时间内完成pos位置的插入删除。唯一的缺点是不支持随机访问,如果要访问list中的第三个元素,只能通过遍历迭代器或者范围for来访问第三个元素。所以list不支持算法库里面的sort(因为算法库中的sort底层是快速排序,快速排序为了防止最坏的情况也就是已排序好的数据,在这种情况下的效率就是O(N^2)因此引入了三数取中,但是如果不支持随机访问,三数取中就不可以使用了)
2024-03-20 14:59:05
865
原创 STL_vector简化模拟—详解深层次深拷贝问题
现代写法的思路是:传参过来的这个值是要赋值给this的,参数不用引用,传参的时候就发生了拷贝构造,构造出现的这个对象的内容是this想要的,所以使用swap进行内容的交换,将this内容给vtmp这个局部变量,并且让vtmp帮忙释放这块旧空间(生命周期结束调用析构函数完成资源清理)如果使用的是memcpy增容后和增容前,这些旧数据指向的是同一块内存空间,但是增容后旧的空间被释放了,所以打印的时候会出现随机值,扩容后的对象声明周期结束的时候,对同一块内存空间释放第二次导致了程序崩溃。
2024-03-16 16:49:30
896
原创 STL_vector详解和迭代器失效问题解释
vector是一个大小动态可变的一个数组的序列容器。动态可变自然就是他的大小可以根据插入的数据多少动态的扩容,来适应存储更多的数据。内存不够了会自动增容。数组也就是说vector可以像数组一样使用下标对每个元素进行高效的访问与修改。并且他也像数组一样在内存之中占用一块连续的内存空间。容器就是说明这个vector可以存储各种类型的数据也可以是其他容器。第一个参数是模板参数,指明了vector可以存储任意类型的数据,第二个参数alloc是空间配置器,也就是内存池。
2024-03-16 16:47:44
816
原创 类和对象(上)--关于面向对象,类的定义,访问限定符,this指针
关于类的定义这里因为C++是兼容C语言的,所以C++给C语言的struct一个新的定义,就是用来定义类,C语言中关于结构体的用法C++这里同样也是兼容的。;;int* _a;int _top;定义一个类实际就是一个类型(花括号括起来的就是一个作用域,类的作用域就叫类域),类里面可以定义成员变量_top等,也可以定义成员函数就是Init和Push。name就是类名,成员变量和成员函数组成了类体。struct毕竟是C语言的产物,C++还有一个关键字class来定义类class stu。
2024-03-16 11:34:53
1056
原创 string类代码详解和写时拷贝
二次修订于date:2024:3:16string类的模拟实现可以加深对于string类的理解。一般实现简单的string类就是string的默认成员函数中的四个,构造函数,拷贝构造,赋值运算符重载,析构函数。
2024-03-16 11:32:19
863
原创 STL string的使用介绍
注意这里size的改变,在windows下缩小的时候capacity是没有跟着变化的,但是在别的编译器就不一定了,因为不同的版本的stl都是按照c++标准来实现的,功能是一样的,但是底层的一些细节是不同的,比如增容的时候是标准扩容二倍还是根据情况。解释一下文档:resize就是将这个字符串调整为n个字符的长度,如果原来的长度大于n,大于n的那一部分就会被移除,也就是将size变为n,如果原来的字符串长度小于n,那么会在原来字符串后面追加字符c使得size达到n,如果没有给c传参,那么c默认是空字符。
2024-03-14 19:41:07
372
原创 C++模板(函数模板和类模板)
/类模板的定义格式template//模板参数列表class 类模板名// 类内成员定义下面来看一下顺序表的定义格式和使用。_a = tmp;_size++;int _size;int main()//显式实例化i++)v1[i] *= 2;i++)
2024-03-14 09:54:23
951
原创 C++内存管理
new和delete在自定义类型上使用的时候,new创建对象会自动调用构造函数,delete释放对象的时候会先调用析构函数清理资源。而malloc和free只是负责开辟空间和释放空间。int _val;delete p2;return 0;
2024-03-14 09:47:55
989
原创 类和对象(下)
3,尽量使用初始化列表进行初始化,因为无论你写不写初始化列表,实际调用构造函数的时候都是先使用初始化列表进行初始化的。4,成员变量初始化的顺序是按照变量声明的顺序的,与你在初始化列表中写的顺序无关。引用类型成员变量和const类型的成员变量的特点是都必须在定义的时候初始化,定义完了之后他们的值就不可以更改了。
2024-03-14 08:56:22
846
原创 类和对象 (中)
对于内置类型,构造函数和析构函数都是不起作用的,对于自定义类型会调用这个自定义类型对象的默认构造和析构函数。对于date这种类,没有涉及到malloc或者指针之间的关系的时候是可以不写的,系统默认生成的拷贝构造函数实现的浅拷贝完全够用,但是到了stack这种类的时候就不能用默认拷贝构造函数了。1,拷贝构造函数和构造函数形成了函数重载,参数是类对象的引用,无返回值2,赋值运算符重载是一个函数名operator=的函数,参数是对象类的引用,返回值是对象的引用,因为这个对象出了函数作用域并不会销毁。
2024-03-13 22:21:06
901
原创 Linux文件系统和软硬链接
所以一个分区内存在多个group里面都是具有Super Block的,这些副本保证了当一个group中的Super Block损坏的时候,只有一个group的数据丢失,想要恢复只需要将其他group组中的Super Block拷贝就可以了。blocks数组中的0-11下标元素指向的都是文件内容所占用的数据块的编号。简单的说文件系统就是一个管理磁盘上未被打开的文件,他们的数据存在哪里,存储数据的时候该怎么划分空间,每个分区的空间使用了多少,创建的新文件该存放到哪里等等关于磁盘上的文件操作的决策问题。
2023-04-07 10:17:45
499
1
原创 Linux中的文件描述符和重定向
子进程进行重定向也是修改的子进程的files_struct 的数据结构,并且这里采用了引用计数,比如此时两个进程都打开了文件标准输出,如果子进程关闭了标准输出,此时其实标准输出子进程是没有资格关闭的,子进程关闭的意思是子进程现在不用标准输出了,此时标准输出的引用计数-1,当引用计数减为0的时候操作系统才会真正去关闭标准输出。当我们的使用子进程进行重定向的时候,内核数据结构中,创建子进程不会再拷贝一份文件对象而是直接让子进程的文件描述符表内保存和父进程同样的地址,指向的是同一份文件对象。
2023-04-06 02:30:00
820
原创 Linux的进程控制
然后让操作系统释放子进程的PCB,子进程僵尸之后子进程的代码和数据是可以被释放的,但是子进程的PCB要保存子进程的退出信息和子进程的状态等等,所以不能立即释放。这是task_struct 也就是PCB中的一部分代码,进程的状态,进程的退出码,进程的退出信号都在这里。因为wait和waitpid是系统调用,所以操作系统有资格也有能力去读取子进程task_struct里面的退出码和终止信号。
2023-04-05 20:29:33
859
原创 Linux进程地址空间
进程认为自己独占系统资源其中当然包括CPU,当进程运行的时候,只有一个进程在CPU上跑,此时进程当然认为自己是独占CPU的,当进程的时间片结束,从CPU上剥离下来的时候此时进程从运行状态变成了休眠状态,变成休眠状态的进程就不知道外面发生了什么了,所以进程是不知道自己被从CPU下剥离下来了。也可以叫做线性地址,逻辑地址。在开篇举例中,fork创建子进程之后,父子进程一开始打印的g_val其实是同一个g_val,因为创建子进程的时候也需要创建PCB和进程地址空间,子进程的创建一般都是拷贝父进程的。
2023-03-17 04:00:00
408
原创 Linux下的环境变量详解
通过export直接添加环境变量,让环境变量直接等于,后面的33333其实是被看作是字符串的,所以我们不用写双引号也是可以的,但是如果我们的环境变量里面有空格,此时就需要用双引号括起来标识一个整体了。getenv的参数就是一个字符串,这个字符串是环境变量名,返回值是一个字符串,如果name环境变量存在,返回值就是环境变量的内容,如果name环境变量不存在返回的就是NULL。环境变量有很多种,是操作系统为了满足不同的应用场景,在操作系统启动bash做命令行解释的时候,预先设置好的一批未来可能会用到的变量。
2023-03-16 13:30:00
1869
原创 进程概念总结
在操作系统的书上是这样描述进程的:一个运行起来(加载到内存)的程序就是进程,进程具有动态属性。程序是什么?程序的本质就是磁盘上的一个文件。当程序被加载到内存之后,假设内存中有很多这样的进程,那么此时操作系统需要多这么多的进程进行管理,如何管理呢?先描述,在组织。程序在载入内存之后在内存中不仅有程序的代码和数据,还有一个对应的PCB。
2023-03-09 22:52:23
806
原创 操作系统简介
操作系统对上提供系统调用接口,给用户层的用户进行各种操作所使用,但是不是所有的用户都会调用系统调用接口,所以为了方便用户使用出现了shell外壳,将我们的指令操作变成系统接口的调用,lib是我们写代码的时候将需要调用的系统接口封装成库函数。描述就是将要管理的大量数据抽离出基本的公共信息种类,组成一个结构体,再组织就是将通过结构体创建出来的对象用数据结构组织起来,这样我们对硬件的管理就变成了对数据的结构的管理。操作系统最终是通过合理的管理软硬件资源(手段)为用户提供良好的(稳定,安全,高效)的运行环境。
2023-03-01 02:45:00
219
原创 冯诺依曼体系结构
计算机也是类似的,CPU就像是那个最长的木板,如果在进行数据交互的时候每次CPU都和外设交互,那么CPU那么快的速度就没用了。首先你将消息通过键盘输入,载入到内存中属于QQ的那块空间,然后通过CPU对消息进行处理(加密之类的)然后再写回内存中,内存将数据写到网卡中,通过网络发送到网友电脑的网卡,然后将接受到的数据写入内存,CPU处理后再写回内存,从内存再写到显示器上。且当内存中的数据要往磁盘的写的话是什么时候写的(肯定不是随时写的,太慢了)也就是缓冲区是什么时候清空的呢。存储器也就是内存,具有掉电易失性。
2023-02-28 21:55:50
3951
原创 Linux开发工具(6)——gdb
为什么程序要有debug和release版本呢,首先debug包含调试信息,程序员可以对程序进行调试,但是用户不需要进行调试,因为我们不能给用户他不需要的东西,所以release版本呢就是去掉了调试信息并优化了可执行程序的速度。当我进入Add函数中的时候,使用finish就可以直接跑完Add函数的逻辑,并且显示函数的参数,所在文件中的行号,函数地址,以及该函数在主函数中调用的位置,还有返回值是多少。当我们使用display将i变量常显示之后,随着代码的执行,i变量的值也会跟着变化。输入y就重头开始了。
2023-02-22 07:49:38
428
原创 Linux开发工具(5)——git
比如你在写一个小程序,你写了三天后第一次测试,有一些bug但是逻辑是没有问题的,所以你上传了git,又进行了几天的bug修复,结果测试完了之后bug更多了。所以远端仓库如果发生了改变,此时你当前用户的仓库如果没有更新,那么此时你是无法将文件push到远端仓库的,必须要先pull同步到本地,再push才可以。标题也说了git就是一个版本控制器,版本控制器是用来保存一个文件的历史版本,如果有需要可以进行回溯,也就是取得以前编辑完成的版本。现在文件的修改已经同步到本地仓库,现在就可以push到远端仓库了。
2022-11-19 03:00:00
679
原创 Linux开发工具(4)——Makefile
make只有在第一次编译的时候才会进行更新,后序就不会进行更新因为没有对源文件进行修改自然不需要再重复编译,这是gcc的特性,因为在大型的项目中,编译一个项目少则几十分钟,多则几个小时,所以重复编译没有修改的代码就会造成时间的浪费。\n和\r的作用和区别,\r的作用时回车,\n的作用的换行,回车的意思就是将光标退回到这一行的开始,换行就是将光标移动到下一行,所以我们平时说的语言层面的回车是指\r + \n回车加换行。这张图的命令执行顺序也说明了,Makefile的推导顺序是栈结构的先进后出。
2022-11-19 01:00:00
1663
原创 Linux开发工具(2)——vim
表示强制保存,退出的时候如果退出不了也可以使用q!虽然说vim的配置文件是每个用户都有一个的也就是说每个用户的vim配置是不一样的,但是我们也可设置一个全局的vim配置给所有的用户都进行vim的配置,这个配置文件的路径就是/etc/vimrc,vimrc就是这个全局的vim配置文件。更多具体的配置我们可以百度出来,然后将对应的选项加入.vimrc文件保存之后,如果添加的指令没有在vim里面生效,可以输入source .vimrc让配置生效一下,如果还是不行,那说明你设置的选项在当前的vim下不支持。
2022-11-18 03:30:00
598
原创 Linux开发工具(1)——yum
现在还有一个问题就是,Linux社区这些服务器都是在国内的,那么软件包的下载链接也都是链接到国外的服务器,有些在我们国内是无法访问的,因此,国内的一些企业比如阿里,腾讯,百度,包括一些高校比如清华等,他们会将这些国外的软件资源镜像到国内的服务器上,他们会提供一套国内的下载链接配置文件(yum源)我们只需要通过配置yum源即可。这里系统自带的都是官方的软件集合,该集合内的软件都是比较稳定成熟的软件,还有一些软件他们是测试软件,所以还没有收录到官方软件集合里面,我们也可以下载下来这个软件集合的yum源。
2022-11-18 03:00:00
670
原创 Linux开发工具(3)——gcc/g++
预处理后的文件,从原来的几十行变成了八百多行,多出来的内容都是头文件stdio里面展开的内容,宏被直接替换到代码里面,条件编译也完成了。并且预处理后的文件还是C语言的。在使用gcc进行链接的时候如果只有一个动态库或者一个静态库,那么就用这个唯一的库进行链接,如果两个库都有,那么默认使用动态库。动态库:libXXXXX.so (以lib开头和.so结尾的库就是动态库,去掉lib和.so的后缀,剩下的就是库的名称。C语言的动态库虽然被很多程序都使用但是动态库只有一个,所有的程序都是共用c标准库的。
2022-11-17 23:02:50
981
原创 Linux文件权限
使用一个例子来介绍权限,比如我们平时在使用爱奇艺看视频的时候,有些视频只有会员才能看,如果你不是会员那么你就被约束了不能看这个视频。所以说这里的权限的组成有两部分,角色和对象,首先是如果一个对象不具有某种属性那么不管是什么角色都不会有这个权限,比如爱奇艺没有玩游戏的属性,所以任何角色都不能在爱奇艺上玩游戏,任何角色都没有这个权限。只有当某个对象本身具有某种属性,某个角色才会被允许拥有某种权限。权限是约束某种角色的一般是一个人或者一个群体。某种行为允许被谁做?
2022-11-15 03:00:00
6329
原创 shell命令和运行原理
第二个意义,在Linux中我们有时候会出现输入错误的指令,这些指令就是非法指令,shell对于我们输入的命令进行合法性分析,然后可以将非法指令挡在操作系统的外面,这样操作系统就可以只关于合法指令的执行了。原因是:用户输入的某些非法指令虽然会被挡在操作系统之外,但是shell可能会因为某些指令挂掉,但是shell程序是不可以挂的,一旦shell外壳程序挂掉了,那么用户就无法与操作系统进行交互了。执行指令的时候,shell是通过派生子进程的方式执行用户的指令,而shell本身并不直接执行指令。
2022-11-04 08:11:30
500
原创 数据结构<7> 图
图是一种非线性结构,前面说过的一种非线性结构就是树,树是由节点组成的具有跟单额分层结构,各个顶点之间是父子关系,一个顶点最多有一个父节点,但是可以有0个到任意多个子节点。图的节点与树不同,图的每个节点可以与任意个其他顶点相连。各个顶点之间的关系是任意的。由此我们也可看出图的表示范围是要比树大的,树是一种特殊的图。
2022-10-28 11:52:48
713
原创 数据结构<6> 并查集
但是在b集合合并到a集合的时候就会发现,此时2000个点的高度+1,因此将节点少的集合,合并到节点多的集合有利于降低查找的时间复杂度。这里因为我们初始化用的是i,所以,数组存储的额外信息,集合的个数就没有了。这里使用的并查集的写法初始情况是全都置为-1的。首先上面的问题我们可以使用朴素算法来接近,就是开一个和人数相同的数组,然后每个人对应一个位置,这个位置存放的数就是代表这个人属于那个集合。按秩合并,意思就是说,要合并两个集合的时候,将元素个数较小的那个结合合并到元素个数较大的那个集合当中去。
2022-10-23 21:43:23
433
原创 C++11新特性解析
C++11就是C++在继C++98之后推出的第一个添加了重大改进的版本,相比于C++98增加了很多有用的特性,但是C++11从出来开始也饱受诟病,因为C++11不仅增加了很多有用的特性也增加了很多无用的特性,这就无型之中增大了C++语言的学习难度。相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,
2022-10-15 21:35:03
878
原创 C++STL哈希表
哈希算法是如何不通过比较就能确定一个数据在不在呢?答案是:通过将数据插入的位置与关键字建立映射关系,这种映射关系就是后面要说到的哈希函数简单举个例子:现在有这样一个场景,给你了一个长度为N的字符串,需要你在O(N)的时间复杂度内找出所有只出现过一次的字符。这时候我们就可使用哈希算法中的直接定址法来解决。什么是直接定址法呢?就是我们创建一个长度为128的计数数组,然后用字符的ascii码值直接映射到数组的这个位置,对这个位置进行++。
2022-10-08 09:18:22
1750
原创 数据结构<5>二叉树和堆——原理+实现
前面所讲到的数据结构比如:顺序表,链表,栈,队列都是属于线性结构,但是到了二叉树这里就是非线性结构了。下面我们先来认识一下什么是树。
2022-09-28 20:13:17
303
原创 二叉搜索树
迭代法,就需要在找到min节点的时候保存min节点的parent,然后可以将min节点的值与被删除节点的值交换,也可以直接用min节点的值覆盖被删除节点,然后将parent与min节点的右子树连接起来就行(min的左子树为空,所以肯定是连接右子树)如果找到了和插入的值相同的节点则说明插入失败。删除这里的主要部分就是找到了节点之后如果节点的左右子树都不是空,那么使用替换法删除,递归法的替换法删除简单一点,因为只需要找到min节点交换后,递归来到被删除节点的右子树查找key值然后删掉就可以了。
2022-09-20 11:36:03
1037
1
原创 C++——多态
多态是什么,直接看就是多种形态。具体细讲就是不同对象在完成同一种行为的时候结果不同。很常见的一种情况就是不同的对象,调用同一个函数,但是出现了不同的结果.1.静态多态:函数重载(看起来调用的是一个函数但是传递不同的参数有不同的行为,cout的自动识别)2.动态多态:一个父类对象的引用或者指针去调用一个函数,传递不同的对象会出现不同的行为静态:是指在编译阶段实现动态:是指在程序运行阶段实现被virtual修饰的函数就是虚函数public :} };这里的func1就是虚函数。
2022-09-15 12:36:56
985
2
原创 C++——面向对象三大特性之继承
继承的核心是代码的复用,继承机制就是面向对象语言对于类保持原来的结构,并对其进行扩展的重要手段。在继承里面有这样两个类,基类(父类),派生类(子类),根据名称可以看出他们之间的关系。继承就是子类继承父类,子类的结构相当于是在父类的结构上面的一个扩展。继承是类在设计层次的复用。继承可以说是C++语法复杂的一个方面,因为有了多继承,所以就会存在菱形继承,为了解决菱形继承又有了菱形虚拟继承。不仅底层实现复杂,对性能也有影响。所以一般不能设计出菱形继承。大多数情况都是使用单继承。像是java只有单继承。
2022-09-08 19:20:58
577
原创 queue和priority_queue使用+模拟实现
deque与vector比较,头部的插入和删除不需要移动数据效率更高扩容的时候因为map数组里面存放的是指针,所以在扩容的时候的拷贝会比vector拷贝全部数据的效率高很多。与list相比,存储空间是连续的,空间利用率高,同时缓存命中率高,因为连续的数据空间,每次缓存加载的时候都是加载一块空间而不是单个数据空间。但是,
2022-09-03 04:00:00
347
原创 stack使用+模拟实现
思路:使用优先级队列,优先级队列实际就是将vector封装然后加上堆的算法,实际就是一个堆,用nums构建一个优先级队列,默认是大堆,但是这里需要小堆,用前k个数字构建一个小堆,然后遍历剩下的数字,如果遇到比堆顶元素大的那么就删除堆顶元素然后插入新元素,最后这个堆里面的k个数就是整个nums里面最大的k个,第k大的数字就是这个堆里面最小的数字。3.如果该运算符的优先级低于栈顶的运算符,那么出栈顶的运算符,该运算符继续和栈顶的运算符比较,直到该运算符优先级大于栈顶元素,或者栈为空,就将该运算符入栈。
2022-09-01 20:40:10
269
原创 数据结构<4>栈和队列——原理+实现
队列只能用链表来实现,数组实现栈效率太低,例如:将数组0下标位置当作是队头,那么出队列的时候就要将后面的数据都向前移动时间复杂度是O(N),如果将0下标位置当作是队尾,那么每次插入数据都需要向后移动已经插入好的数据,所以数组不适合用来实现队列。循环队列是一种队列的变形,也就是将队列的首位链接起来成为一个环状的队列,循环队列的长度是一定的,一旦确定了元素个数也就确定了环的大小,这个环就不可以在改变了。队列是一种先进先出(FIFO)的结构,结构的一端是队头另一端是队尾,入数据只能在队尾,出数据在队头。.....
2022-08-31 19:22:59
623
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人