- 博客(73)
- 收藏
- 关注
原创 【C++11(二)】可变参数模板和 lambda表达式
我们无法直接获取参数包 args 中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。C++11 的新特性可变参数模板能够让我们创建可以接受可变参数的函数模板和类模板,相比 C++98/03 ,类模版和函数模版中只能含固定数量的模版参数,可变模版参数是一个巨大的改进。包装器包装的是什么?仿函数类型比较好写,但是它比较重,它得在全局里面单独定义一个类,即使是写一个很简单的比较,也是需要定义一个类,这种方法太笨重了。
2025-12-05 20:06:19
833
原创 【Linux线程二】线程互斥
大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况变量归属单个线程,其他线程无法获得这个变量。但有时候很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互,多个线程并发的操作共享变量会带来一些问题。usleep(10);elsebreak;return 0;
2025-11-27 22:31:05
994
原创 【Linux线程一】线程概念和线程控制
有了上面的基础,我们现在要重新定义线程和进程的概念。· 线程:我们认为,线程是操作系统调度的基本单位什么是进程呢?我们以前说的进程指的是 task_struct 和代码数据,但现在已经有了分歧,因为它只是地址空间的一个执行分支,一个执行分支不能代表整个进程。所以现在需要重新理解:全部 task_struct 执行流都叫做进程执行流,地址空间都叫做进程所占有的资源,页表和该进程所占用的物理内存,我们把这全部一整套总称为进程。· 进程:进程是承担分配系统资源的基本实体那么执行流是资源吗?是的。
2025-11-17 17:40:25
962
原创 【Linux信号二】信号保存与信号捕捉处理
信号为什么要保存?因为进程收到信号之后,可能不会立即处理这个信号,可能正在处理更重要的事情,所以信号不会被处理,就要有一个时间窗口,所以信号就要被保存。
2025-11-16 11:00:42
1145
原创 【Linux信号一】信号概念与信号产生
信号是进程之间事件异步通知的一种方式在Linux命令行中,我们可以通过ctrl + c来终止一个前台运行的进程,其实这就是一个发送信号的行为我们按下ctrl + c是在shell进程中,被终止的进程是在前台运行的另外一个进程,因此信号是一种进程之间的通知方式进程必须识别并处理信号,并且信号没有产生,也要具备处理信号的能力。
2025-11-06 17:19:21
1314
原创 【Linux进程间通信二】System V 共享内存和消息队列
所以,它们必须区分开来,区分方式就是向内核发送的数据块是带类型的!共享内存实际上被申请的时候,它有自己的管理属性,那么它自己会记录共享内存有多大,共享内存也必须是连续的,所以在进行地址空间映射的时候,从连续空间加上大小,我们就知道它的范围了,我们只需要知道从哪开始就行了。system V作为一种通信标准,在此标准下有一套共享内存机制,即通过系统接口得到一块共享内存,这块内存就对应着通信前提中的同一份资源,让不同的进程都“看到”这块资源后,不同进程就可以进行通信,与管道一样,共享内存也是一种IPC技术。
2025-10-25 17:14:49
1142
原创 【C++11(一)】列表初始化和右值引用
虽然说我们的左值引用,也可以达到这样的移动构造,但是有一个问题,并不是所有的对象,资源都是可以被转移走的。移动构造之所以这么叫,就是因为移走了别人的资源。自定义类型在作为函数返回值或者隐式类型转换时,会产生一个临时对象,这个临时对象具有常属性,即不能修改,不能取地址,是一个右值,临时变量的作用是:作为中间值,将自己的数据赋值给其他变量,赋值完成后临时变量销毁,即临时变量的生命周期很短)左值:可以被取地址,可以对它赋值,可以出现赋值符号的左边,const修饰符后的左值,不能给它赋值,但是可以取它的地址。
2025-10-19 15:35:33
889
原创 【Linux基础I/O三】静态库和动态库
当我们的程序编译成为二进制文件之后,变量名、函数名等就没有了,编译的时候,对代码进行编址,基本是虚拟地址空间的概念,所以在程序没有被加载到内存的时候,就已经具有了“虚拟地址”,通俗地说是逻辑地址,这种逻辑地址的概念就是基地址+偏移量(第一次加载:如果动态库尚未被加载到内存中,动态链接器会将该库加载到内存中,并映射到进程地址空间的共享区中)首先动态库是可执行程序和库分离开的,我们的可执行程序加载到内存中了,但是库还没有加载到内存中,而静态库是直接拷贝到可执行程序中的,所以它们会被一起加载到内存中。
2025-10-11 00:01:23
904
原创 【LInux基础I/O二】文件系统
从此以后我们不再关注扇区,站在文件系统角度,只需要关注以4KB为基本单元的Blocks的数组即可,每一个4KB文件块都有它的LBA地址,从此往后,对于磁盘的管理和对于文件系统的管理,就转换成了对该数组进行管理。文件的所有的数据,包括内容和属性,都存在盘面上,磁头通过左右摆动读取和写入数据,而盘片通过顺时针或逆时针转动,配合磁头的左右摆动,就可以读取整个盘面的数据,我们我们看到的盘面是光滑的,其实它上面是凹凸不平的,因为它上面有许多分区。索引,从而找到更多的数据块,从而将空间变大,可以容下更大的文件。
2025-10-01 18:57:37
980
原创 【C++高阶八】哈希的应用—位图和布隆过滤器
哈希最常用的应用是unordered系列的容器,但是当面对海量数据,如:如果要使用unordered_set来解决40亿个整数,一个整数占4四节,总共大约占16个G的内存空间,并且set容器中不止有整型数据还有其他的数据,所以需要另一种解决方法,而一个数在或不在可以用1/0来表示,也就是说只需要一个比特位就可以知道一个数在不在其中,于此诞生了位图位图就是用每一位来存放某种状态,适用于海量数据且数据无重复的场景,通常是用来判断某个数据存不存在的。
2025-09-04 16:45:21
1500
原创 【C++高阶七】unorder_set和unorder_map封装
unordered_set的使用:大部分功能与set基本相同,要注意的是unordered_set是无序的,插入数据,并使用迭代器打印,会按照插入的顺序输出,但若插入的数据已经存在,则会插入失败。在STL中,是不允许 unordered_set去 *it 修改数据的 ,在STL中将 unordered_set的普通迭代器也为哈希桶的const 迭代器。在unordered_set中,使用哈希桶中的HashTable的迭代器 来实现unordered_set的迭代器。
2025-08-28 17:43:16
847
原创 【C++高阶六】哈希与哈希表
理想中的搜索方法:不经过任何比较和遍历就可以直接从表中搜索想要的元素如果构造一种存储结构,并通过某种函数能使元素的存储位置与它自身关键码之间建立映射的关系,那么在查找时只要通过函数就可以快速找到该元素当向该结构中插入元素时,根据待插入元素的关键码,以函数计算出该元素的存储位置并按此位置进行存储当向该结构中搜索元素时,用函数对元素的关键码进行同样的计算求得元素的存储位置,在结构中按此位置取元素比较,若关键码相等则搜索成功。
2025-08-22 18:02:24
861
原创 【C++高阶五】map&set对红黑树的封装
C++的STL库中把红黑树封装为了两个容器与本博客将基于红黑树来实现map和set的封装如果不了解红黑树,可见博客。
2025-08-08 19:46:31
1211
1
原创 【C++高阶四】红黑树
上篇博客学习了平衡二叉搜索树(AVLTree),了解到AVL树的性质,二叉搜索树因为其独特的结构,查找、插入和删除在平均和最坏情况下时间复杂度都是O(logn)。但在AVL树中插入或删除节点时,高度差绝对值大于1,平衡被破坏,为了重新维持其稳定,要进行旋转处理,但因为每个结点的高度差的绝对值都要小于1这个条件较为的严格,导致多数情况的插入和删除都需要旋转调整,导致插入和删除的效率降低。
2025-07-16 17:31:17
649
原创 【C++高阶三】AVL树深度剖析
AVL树也叫二叉搜索平衡树因为二叉搜索树如果插入顺序是有序的,那么这棵树的查找效率将会是O(N),所以说在实际情况下,二叉搜索很少被使用为了解决这个缺点,诞生了AVL树一颗高度不平衡的树:一颗AVL树:一般的二叉搜索树在插入新节点以及删除节点时,都有可能会破坏树的平衡,所以AVL树需要对插入以及删除接口做修改,每次插入删除时,都要检测一下当前的树时候符合AVL树,如果不符合,要做出相应的调整措施由于AVL树的这种特殊性质,使得它的查找效率是百分百的O(logn)2.2插入插入有三个步骤:
2025-06-19 13:49:12
432
原创 【C++高阶一】二叉搜索树
任何一个节点,他的左子树的所有节点都比他小,右子树的所有节点都比他大二叉搜索树是有序的,中序遍历这棵二叉搜索树刚好是升序时间复杂度为O(N),因为会出现这种极端情况二叉搜索树中不能出现值相同的节点。
2025-05-26 18:56:50
771
原创 【Linux基础I/O】文件调用接口、文件描述符、重定向和缓冲区
文件描述符的分配规则:查看自己的文件描述表,分配最小的没有被使用的fdprintf和scanf作为C语言的函数接口,同样无法直接和显示器和键盘作交互,其必须依靠相应的文件,也就是标准输出和标准输入但是依靠的指向并不是FILE*,而是文件描述符,printf依靠1号文件,scanf依靠0号文件int main()close(1);//关掉1号文件(stdout)//打开log.txt//向屏幕打印return 0;可以看到。
2025-05-21 23:28:43
990
原创 【Linux进程控制二】进程替换和bash解释器
其他接口就不一一演示了int main()printf("程序替换前\n");//自动查找可执行文件并执行,但我们主动传递了文件路径也不会出错printf("程序替换后\n");return 0;虽然使用的是execvp,但我们主动传递了文件路径也不会出错。
2025-05-14 23:14:04
1228
原创 【Linux进程控制一】进程的终止和等待
进程等待用于回收子进程的资源,避免子进程的PCB一直占用资源,并且可以获取子进程的退出信息,得知子进程任务的执行情况,进程等待主要通过两个系统调用接口wait和waitpid来完成。
2025-05-09 21:14:42
1179
原创 【C++进阶十】多态深度剖析
overrride:检查子类虚函数是否重写了父类的某个虚函数,如果没有重写编译报错final:修饰虚函数,表示该虚函数不能被重写sizeof(Base) 大小是多少?以结构体的内存对齐考虑,在32位机器下,大小应为8字节,但是实际上为12字节_vfptr代表虚函数表指针加上虚表指针,内存对齐后字节大小为12。
2025-05-05 16:56:13
1032
原创 【C++进阶七】栈和队列及优先级队列深度剖析
deque又被称为双端队列,是一种双开口的“连续"空间的数据结构,双开口意味着可以在首尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高且不需要搬移元素,与list比较,空间利用率较高。在C语言阶段实现栈时,底层是使用顺序表来实现的,把顺序表做了一层封装和限制让它的功能变得和栈一样,C++实现栈也是同理。但是栈和队列的实现非常特殊,它的内部结构并不是靠自己实现的,而是一种适配器模式。学习C++和C语言的顺序一样,顺序表、链表之后就是栈和队列。队列的front相当于栈的top。
2025-04-15 17:45:53
369
原创 【C++进阶六】list模拟实现
因为list中的++实际上是node=node->next,list中的==符号判断的是node1->data和node2->data是否相同,如果迭代器和。list的迭代器底层不是简单的指针,所以我们不能像实现vector一样直接在类中定义迭代器和使用迭代器相关函数。begin() 代表第一个结点,end()代表最后一个结点的下一个。重新写一个类的作用是将迭代器的++和- -等操作实现在此类中。同一个类模板,实例化参数不同,就是完全不同的类型。我们需要单独实现一个新的类叫做。模拟const T*
2025-04-14 18:42:35
1169
原创 【C++进阶五】list深度剖析
STL标准库中的list是一个带头双向循环链表和vector不同,list没有支持[ ]访问以及resize和reserve容量相关的函数,这是因为list不能随机访问数据,并且list的迭代器的底层不是指针和vector一样,list也有两个模板参数,但是第二个模板参数是和内存效率相关的,所以现在的学习暂时不用管它(它有缺省值)
2025-04-05 17:54:32
1272
原创 【C++进阶九】继承和虚继承
继承是代码复用的一种手段,子类继承父类就能使用父类中的变量比如我们同时定义student类和teacher类二者有大量的相同信息,因此可以把共同信息提取出来单独形成一个类string sex;int age;int height;只用在实现student类和teacher类时继承person类即可class Student : public Person//继承protected:int _stuid;//学号class Teacher : public Person//继承。
2025-04-03 22:03:49
1178
原创 【C++进阶四】vector模拟实现
但是又因为我们实现的size()是_finish - _start,所以_finish=_start+ _finishi - _start = _finish。我们写的push_back拷贝用的是memcpy,对于内置类型,tmp出了作用域也不用处理,但对于自定义类型,tmp除了作用域还要被析构一次,一个空间总共。erase(it)后,it指向位置的意义就改变了,如果是连续的偶数,会导致后一个偶数没有被判断,也没有被删掉。因为begin()是传值返回,返回的是临时变量,具有常性,不能直接传给引用。
2025-04-02 21:11:59
1110
原创 【C++进阶三】vector深度剖析(迭代器失效和深浅拷贝)
迭代器失效的本质原因是:扩容后start和finish的地址发生变化,指向原先位置的迭代器失效。
2025-04-02 18:26:11
1093
原创 【C++进阶二】string的模拟实现
string底层是一个字符数组,为了跟库里的string区别,我们定义了一个命名空间将string类包含咋内。
2025-03-26 18:46:49
1014
原创 【Linux进程七】程序地址空间
我们用下面的代码可以查看到一个事实int main()if(id == 0)//子进程执行的代码tmp = 200;while(1)printf("子进程tmp: %d,&tmp: %p\n",tmp,&tmp);sleep(1);if(id > 1)//父进程执行的代码while(1)printf("父进程tmp: %d,&tmp: %p\n",tmp,&tmp);sleep(1);return 0;子进程和父进程的地址一样,但却有两个值。
2025-03-20 23:13:28
1412
原创 【LInux进程六】命令行参数和环境变量
因为我们修改的环境变量表是bash内部的,每一次重新登陆账号都会依照家目录下的.bash_profile文件中的内容重新生成命令行解释器bash。后面继续输入任意字符串时,它会以空格为分割,分别打印出数组中下标为0,1,2的字符串,这些输出的字符串正是我们所输入的。获取不到本地变量的信息(命令行启动的进程都是shell和bash的子进程,而内置命令是由bash自己执行的)命令行启动的进程都是shell和bash的子进程,子进程的命令行参数和环境变量都是父进程传递的。代表这个数组的元素个数(指针的数量)
2025-03-18 23:31:35
1037
原创 【C++进阶一】STL和string
string是表示字符串的字符串类,该类的接口与常规容器的接口基本相同,在此之上添加了一些专门用来操作string的常规操作。若len长度大于字符pos位置后的长度或者不给len值自动使用了npos, 则pos位置开始全的部删除。s.begin()代表开头的位置,s.begin()+5代表w的位置,在w之前插入字符!查找字符,找到了返回当前pos位置的下标,没有找到就返回npos(整形最大值)顾名思义是倒着走的迭代器,和反向迭代器相对应的是rbegin和rend函数。(像指针一样的类型,用法和指针相似)
2025-03-18 11:56:50
851
原创 【C++基础十】泛型编程(模板初阶)
tmp = a;a = b;b = tmp;tmp = a;a = b;b = tmp;a = b;b = temp;正常来说,对于不同类型的变量进行交换,需要实现不同的swap函数,这样实现有些太繁琐了为了解决相似函数的不同调用问题,C++提出泛型编程,编写与类型无关的通用代码,实现代码复用,即模板模板主要分为函数模板和类模板template <typename T1, typename T2,…,typename Tn>//一次性可以定义多个类型。
2025-03-17 12:29:56
883
原创 【C++基础九】内存管理(new和delete)
new自定义类型用法与内置类型相同在申请自定义类型的空间时,new会调用构造函数,malloc不会new可以初始化变量的内容class Apublic:: _a(a)~A()private:int _a;delete[]p1;return 0;delete会调用自定义类型的析构函数,而free不会delete面对不同的情况需要加上[ ],然而free不用考虑public:stack()//构造_top = 0;
2025-03-14 18:38:51
782
原创 【C++基础八】类和对象—末(初始化列表、友元和匿名对象)
内部类是一个定义在另一个类内部的类,内部类是一个独立的类,不属于外部类,不能通过外部类的对象访问内部类的成员(外部类对内部类,没有任何优越的访问权限)友元函数可以直接访问类的私有成员,它是定义在类外部的函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。构造函数中,为一个成员赋值,只能说对此成员赋初始值,不能称之为初始化,初始化列表才是真正初始化成员变量的地方。一个函数定义在类外,但又想访问类中的私有成员,只能将这个私有成员改成公有后再访问,但这种操作就破坏了类的封装。
2025-03-14 12:02:16
734
原创 【初阶三】认识C语言—下
define定义宏和我们之前的定义常量有一点相似//define定义宏//define定义宏 # define ADD(x , y)((x) +(y)) //Add后面括号里的x和y为参数,后面的((x)+(y))叫做宏的实现体 # include <stdio.h> int main() {//遇见Add()时就会自动把Add和里面的变量替换为后面的宏的实现体 printf("sum = %d\n" , sum);
2025-03-13 18:16:11
829
2
原创 【C++基础七】类和对象—下(拷贝构造和运算符重载)
类的对象d2实例化需要先调用构造函数,所以需要先传参数,把d1传给拷贝构造中d的这个过程属于传值调用,若两者为内置类型(int ,char ,double)会直接进行拷贝,但d1和d都是自定义类型date,所以会发生拷贝构造,默认构造函数是按照字节方式直接拷贝的,array指向的空间是动态开辟在堆区存储的,使用值拷贝的方式,s1和s2中的array指向的空间相同。由于拷贝中的d变成了d1的别名,d相当于d1本身,所以参数d1传给d的过程中, 不会发生拷贝构造。
2025-03-13 13:45:00
971
原创 【C++基础六】类和对象—中(构造和析构函数)
构造函数,,是用于初始化的函数函数名与类名相同无返回值对象实例化时自动调用对应的构造函数构造函数可以重载构造函数是特殊的成员函数,不能将它与普通函数对比构造函数的任务是初始化对象,而不是开辟空间创造对象class Datepublic:Date(int year, int month, int day)//含参的构造函数_day = day;Date()//无参的构造函数_month = 3;_day = 12;
2025-03-12 21:49:19
748
原创 【C++基础五】类和对象—上 (成员函数和this指针)
class className//类的定义与结构体类似,只不过将struct换成了class// 类体:由成员函数和成员变量组成//一定要注意后面的分号声明和定义都放在类里char* name;char* sex;int height;int age;void Print_Info()//打印学生的消息这个类的成员函数的声明和定义都在类中,编译器就可能把此函数当作内联处理只要是在类中定义的函数都会被看作内联(这只是给编译器一建议,具体会不会内当作内联要看代码长度)声明和定义分离。
2025-03-12 11:36:44
1147
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅