C++
文章平均质量分 94
C++学习
sushang~
不积跬步,无以至千里;不积小流,无以成江海。
展开
-
C++进阶--C++11智能指针
智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象生命周期结束时,自动调用析构函数释放资源。RAII是resource acquisition is initialization的缩写,意为“资源获取即初始化”。它是 C++ 之父 Bjarne Stroustrup 提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。原创 2024-02-07 21:15:28 · 3425 阅读 · 0 评论 -
C++进阶--C++11包装器
第一个参数传入函数指针,后面传入绑定的参数列表依次是placeholders::_1和placeholders::_2,表示后续调用新生成的可调用对象时,第一个参数传给placeholders::_1,第二个参数传给placeholders::_2。根本原因就是因为,后续调用新生成的可调用对象时,传入的第一个参数会传给placeholders::_1,传入的第二个参数会传给placeholders::_2,因此可以在绑定时通过控制placeholders::_n的位置,来控制第n个参数的传递位置。原创 2024-02-07 13:03:21 · 880 阅读 · 0 评论 -
C++进阶--C++11线程库
如果共享数据都是只读的,那么没问题,因为只读操作不会影响到数据,更不会涉及对数据的修改,所以所有线程都会获得同样的数据。调用thread的成员函数get_id可以获取线程的id,但该方法必须通过线程对象来调用get_id函数,如果要在线程对象关联的线程函数中获取线程id,可以调用this_thread命名空间下的get_id函数。线程函数的参数是以值拷贝方式拷贝到线程空间中的,就算线程函数的参数为引用类型,在线程函数中修改后也不会影响到外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。原创 2024-02-05 00:01:10 · 1110 阅读 · 0 评论 -
C++进阶--C++11 lambda表达式
因此每个lambda表达式的类型都是不同的,这也就是lambda表达式之间不能互相赋值的原因,我们通过typeid(变量名).name()的方式获取lambda表达式的类型。[=]**的方式捕获变量时,编译器也不一定会把父作用域中所有的变量捕获进来,编译器可能只会对lambda表达式中用到的变量进行捕获,没有必要把用不到的变量也捕获进来。但是由于这里是传值捕捉,lambda函数中对a和b的修改不会影响外面的a、b变量,与函数的传值传参是一个道理,因此这种方法无法完成两个数的交换。原创 2024-02-02 23:55:02 · 1078 阅读 · 0 评论 -
C++进阶--C++11新的类功能和可变参数模板
/ Args是一个模板参数包,args是一个函数形参参数包// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。{}说明模板参数Args前面有省略号,代表它是一个可变模板参数,我们把带省略号的参数称为参数包,参数包里面可以包含0到N(N≥0)个模板参数,而args则是一个函数形参参数包。模板参数包Args和函数形参参数包args的名字可以任意指定,并不是说必须叫做Args和args。原创 2024-01-30 22:18:03 · 772 阅读 · 0 评论 -
C++进阶--C++11右值引用和移动语义
右值引用和移动语义一、基本概念1.1 左值的概念1.2 右值的概念1.3 左值引用的概念1.4 右值引用的概念二、右值引用使用场景和意义2.1 左值引用的使用场景2.2 左值引用的短板2.3 右值引用和移动语义2.3.1 移动构造2.3.2 移动赋值2.3.3 STL容器2.4 右值引用引用左值2.5 右值引用的其他使用场景三、完美转发3.1 万能引用3.2 完美转发保持值的属性3.3 完美转发的使用场景一、基本概念 传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现原创 2024-01-29 16:45:59 · 1124 阅读 · 0 评论 -
C++进阶--C++11入门基础
在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。原创 2024-01-26 16:09:34 · 900 阅读 · 0 评论 -
C++进阶--特殊类设计
在C++类与对象中,C++98中,一个空类中编译器会默认生成六个成员函数,分别是构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、普通对象和const对象取地址重载函数;在C++11中增加了移动构造和移动赋值。而对于一些特殊的类,当用户未显示生成相应的方法时,我们也不希望编译器生成该方法。所以每当设计一个类的时候都得根据用户的需求来进行相应的设计。原创 2024-01-25 14:35:58 · 966 阅读 · 0 评论 -
C++进阶--哈希表模拟实现unordered_set和unordered_map
哈希表里面具体存的是什么类型的元素,是由模板参数T来决定如果是unordered_set,传给T就是Key如果是unordered_map,传给T的就是pair哈希表结点结构T _data;{}迭代器的结构:注意:哈希表迭代器封装的是结点指针和哈希表指针,因为要在哈希表中找下一个桶,所以要用到哈希表指针在构造正向迭代器时,我们不仅需要对应哈希结点的指针,还需要该哈希结点所在哈希表的地址。原创 2024-01-21 21:45:24 · 1199 阅读 · 0 评论 -
C++进阶--哈希表的的闭散列和开散列(哈希桶)实现
与闭散列的哈希表不同的是,在实现开散列的哈希表时,我们不用为哈希表中的每个位置设置一个状态字段,因为在开散列哈希表中,我们将哈希地址相同的元素都放到了同一个哈希桶中,并不需要经过探测寻找所谓的“下一个位置”。与闭散列不同的是,这种将相同哈希地址的元素通过单链表链接起来,然后将链表的头结点存储在哈希表中的方式,不会影响与自己哈希地址不同的元素的增删改查的效率,因此开散列的负载因子相比闭散列而言,可以稍微大一点。闭散列解决哈希冲突,采用的是一种报复的方式,”我的位置被占用了我就去占用其他位置“。原创 2024-01-20 22:01:01 · 1050 阅读 · 0 评论 -
C++进阶--空间配置器
内存池就是:先申请一块比较大的内存块已做备用,当需要内存时,直接到内存池中去取,当池中空间不够时,再向内存中去取,当用户不用时,直接还回内存池即可。一级空间配置器原理非常简单,直接对malloc与free进行了封装,并增加了C++中set_new_handle思想。虽然在常规使用STL时,可能用不到它,但站在学习研究地角度,学习它地实现原理对我们有很大地帮助。,但没有采用链表的方式对用户已经归还的空间进行管理(因为用户申请空间时在查找合适的小块内存时效率比较低)而是。空间配置器,顾名思义就是。原创 2024-01-18 14:20:19 · 902 阅读 · 0 评论 -
C++进阶--IO流
在C语言中,如果想要将一个整形变量的数据转化为字符串格式,如何去做?1.使用itoa()函数2.使用sprintf()函数但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。在C++中,可以使用stringstream类对象来避开此问题。在程序中如果想要使用stringstream,必须要包含头文件。原创 2024-01-17 19:37:06 · 1167 阅读 · 0 评论 -
C++进阶--类型转换
而使用dynamic_cast进行向下类型转换则是安全的,如果父类的指针(或引用)指向的是子类对象,那么dynamic_cast会转换成功,但如果父类的指针(或引用)指向的是父类对象,那么dynamic_cast会转换失败并返回一个空指针。2.由于编译器认为const修饰的变量是不会被修改的,因此会将const修饰的变量存放到寄存器当中,当需要读取const变量时就会直接从寄存器中进行读取,而我们修改的实际上是内存中a的值,因此最终打印出a的值是未修改之前的值。原创 2024-01-17 13:30:18 · 1066 阅读 · 0 评论 -
C++进阶--异常
实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。最基础的异常类至少需要包含错误编号和错误描述两个成员变量,甚至还可以包含当前函数栈帧的调用链等信息。该异常类中一般还会提供两个成员函数,分别用来获取错误编号和错误描述。原创 2024-01-16 22:03:05 · 821 阅读 · 0 评论 -
C++进阶--unordered_set、unordered_map的介绍和使用
1.unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。2.在unordered_set中,元素的值同时也是唯一的标识它的key。3.在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。4.unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。5.它的迭代器至少是前向迭代器。原创 2024-01-15 21:22:01 · 4031 阅读 · 0 评论 -
C++进阶--红黑树模拟实现STL中的map和set
因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。但是对于底层红黑树来说,它并不知道上层容器是map还是set,因此需要进行两个结点键值的比较时,底层红黑树都会通过传入的仿函数来获取键值Key,进而进行两个结点键值的比较。这样一来,当底层红黑树需要进行两个结点之间的键值的比较时,都会通过传入的仿函数来获取相应结点的键值,然后再进行比较。原创 2024-01-15 19:49:53 · 1305 阅读 · 0 评论 -
C++进阶--红黑树
这里直接实现K V模型的红黑树,为了方便后续的旋转操作,将红黑树的结点定义为三叉链结构,除此之外还新加入了一个成员变量,用于表示结点的颜色。,_kv(kv),_col(RED){}这里使用枚举来定义结点的颜色,这样可以增加代码的可读性和可维护性为什么构造结点时,默认将结点的颜色设置为红色?当我们向红黑树插入结点时,若我们插入的是黑色结点,那么插入路径上黑色结点的数目就比其他路径上黑色结点的数目多了一个,即破坏了红黑树的性质4,此时我们就需要对红黑树进行调整。原创 2024-01-14 15:01:51 · 935 阅读 · 0 评论 -
C++进阶--AVL树
我们这里直接实现KV模型的AVL树,为了方便后续的操作,这里将AVL树中的结点定义为三叉链结构,并在每个结点当中引入平衡因子(右子树高度-左子树高度)。除此之外,还需编写一个构造新结点的构造函数,由于新构造结点的左右子树均为空树,于是将新构造结点的平衡因子初始设置为0即可。//三叉链//存储的键值对int _bf;// 平衡因子(balance factor)//构造函数,_kv(kv),_bf(0){}注意。原创 2024-01-12 17:27:51 · 957 阅读 · 0 评论 -
C++进阶--map和set的介绍及使用
set是按照一定次序存储元素的容器在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。set在底层是用二叉搜索树(红黑树)实现的。注意。原创 2024-01-07 16:29:43 · 923 阅读 · 0 评论 -
C++进阶--二叉树进阶(二叉搜索树)
二叉树进阶(二叉搜索树)一、二叉搜索树1.1 二叉搜索树的概念二、二叉搜索树的结构2.1 结点结构2.2 树结构三、二叉搜索树的操作(非递归)3.1 二叉搜索树的插入3.2 二叉搜索树的查找3.3 二叉搜索树的中序遍历3.4 二叉搜索树的删除四、二叉搜索树的操作(递归)4.1 二叉搜索树的查找4.2 二叉搜索树的插入4.3 二叉搜索树的删除五、其他相关成员函数的实现5.1 析构函数5.2 构造和拷贝构造5.3 赋值重载6、二叉搜索树的应用6.1 K模型6.2 KV模型6.3 KV模型代码实现6.3.1 将二原创 2023-12-30 20:56:11 · 989 阅读 · 0 评论 -
C++进阶--继承和多态常见题目
B和C都是虚继承,D继承B和C,那么D也是虚继承,整个继承体系就一份A,针对D对象,对于A的初始化不在B也不在C中,所以初始化列表的B、C、A中,首先应该初始化的就是A。版本二:当子类继承了父类的虚函数并完成重写,则就构成了多态,其底层是父类和子类都有一个虚表指针指向一个虚表,这个虚表是用来存放虚函数的地址的(不是真正的地址,可以理解为间接地址),当父类的指针和引用来调用虚函数时,取决于对象本身(即接收的父类就调用父类,接收的子类就调用子类),父类和子类就会分别去各自的虚表指针里找到相应的虚函数。原创 2023-12-29 17:26:37 · 1100 阅读 · 0 评论 -
C++进阶--多态
被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。必须通过基类的指针或者引用调用虚函数。虚函数就是在成员函数之前加一virtual关键字。虚函数的目的就是为了实现多态。public:virtual void BuyTicket() { cout原创 2023-12-29 17:23:49 · 929 阅读 · 0 评论 -
C++进阶-继承
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。public:protected:// 姓名// 年龄protected:int _stuid;// 学号protected:int _jobid;// 工号int main()原创 2023-12-23 14:20:32 · 912 阅读 · 0 评论 -
C++初阶-模板进阶
array类与vector、list类似,都属于内部类,而上述我们自己写的Array类就是这个类的简化,只不过只有成员变量没有别的东西。array对标的不是vector,而是C语言中的静态数组,例如array a对标int a[N],他们的数据都在栈上,只是前者是自定义类型,后者是内置类型。既然有了array替代静态数组,那么它的优势是什么?最主要的不是封装,而是对于越界的检查。之前学过C语言,对于一个普通内置类型的数组,一旦发生越界访问,就会有这么两种情况:1.越界读,不检查。原创 2023-12-20 16:12:55 · 1077 阅读 · 0 评论 -
C++初阶-反向迭代器的模拟实现
我们反向迭代器的思路是复用正向迭代器的功能,使用一个正向迭代器来创建一个反向迭代器,如果是vector的正向迭代器,创建的就是vector的反向迭代器,如果是list的正向迭代器,创建的就是list的反向迭代器。我们之前实现普通迭代器是声明了一个迭代器类。此时,我们也要定义一个反向迭代器类。而这个反向迭代器是由正向迭代器构造而来的。:_cur(it){}在list类中,我们要重命名一个反向迭代器,并将其调用接口写好。其中关于模板参数的书写,我们要注意一下。第一个模板参数是普通迭代器类型。原创 2023-12-19 12:34:48 · 592 阅读 · 0 评论 -
C++初阶--priority_queue(优先级队列)的使用与模拟实现
优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。原创 2023-12-18 21:38:16 · 997 阅读 · 0 评论 -
C++初阶-queue的使用与模拟实现
1.队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。2.队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。3.底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:empty:检测队列是否为空size:返回队列中有效元素的个数front:返回队头元素的引用back:返回队尾元素的引用。原创 2023-12-17 21:31:37 · 1008 阅读 · 0 评论 -
C++初阶-stack的使用与模拟实现
1.stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。2.stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。3.stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:empty:判空操作back:获取尾部元素操作。原创 2023-12-17 18:09:55 · 930 阅读 · 0 评论 -
C++初阶-list类的模拟实现
对于赋值重载,与拷贝构造相似,但是赋值重载在传递参数时使用传值传参,这样就自动帮我们构造了一个临时对象,我们只需要swap一下临时对象的头节点即可,将我们现在的链表交给临时对象销毁,这样就完成了赋值。因为是带头的链表,所以在实例化时必须事先申请一个头结点,所以所有的构造函数都会在操作前申请一个头结点,为了避免代码的冗余,我们将头节点的创建封装为一个函数,在构造函数初始化时调用创建头结点。节点类不需要拷贝构造和析构函数,对于节点的拷贝构造在list中只需要浅拷贝即可,节点的释放也不在节点类中进行!原创 2023-12-16 22:03:35 · 1239 阅读 · 0 评论 -
C++初阶-vector类的模拟实现
因为增容中使用了memcpy,memcpy导致了问题的出现,因为memcpy是按字节拷贝的,它本质上造成了浅拷贝问题,memcpy使新旧空间都指向了一份数据,旧空间释放后,它指向的对应数据也被释放,而新空间指针还是指向这份旧空间,这就造成了非法访问,所以新空间与旧空间不应该指向同一份数据,应该不用memcopy,写成。(1)释放原空间,新开一块容量和v一样大的空间,更新_start,_finish,_end_of_storage。原创 2023-12-13 18:32:38 · 1000 阅读 · 0 评论 -
C++初阶-list的介绍及使用
list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。原创 2023-12-11 15:39:33 · 991 阅读 · 0 评论 -
C++初阶-vector的介绍及使用
string底层是一个字符数组,模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。(constructor)构造函数声明接口说明vector() (重点)无参构造构造并初始化n个val(重点)拷贝构造使用迭代器进行初始化构造。原创 2023-12-09 16:30:11 · 1129 阅读 · 0 评论 -
C++初阶-string类的模拟实现
若写成两个构造函数,一个设置成无参,一个设置成带参,若调用如上的带参构造函数就会报错,将str传给_str,属于权限放大,为了解决这个问题,可以将_str改为const char*类型,但是无法修改_str所指向的内容,调用operator[]函数就会报错。为了避免频繁扩容,使用一个128的字符数组接收,若输入的数据比128小,跳出循环将数组中的数据传给string类s,若输入的数据比128大,则将字符数组整体传给string类s,再正常扩容。原创 2023-12-09 12:30:36 · 1049 阅读 · 0 评论 -
C++初阶-string的使用
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本。最后:还有一个指针做一些其他事情。原创 2023-12-05 21:03:00 · 1177 阅读 · 0 评论 -
C++初阶-模板初阶
class 类模板名// 类内成员定义// 动态顺序表// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具public :, _size(0){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表if(_pData)原创 2023-11-16 16:52:21 · 150 阅读 · 0 评论 -
C++初阶-内存管理
int main()// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数free(p1);delete p2;// 内置类型是几乎是一样的// Cfree(p3);delete p4;free(p5);return 0;原创 2023-11-16 15:55:09 · 171 阅读 · 0 评论 -
C++初阶-类和对象(下)
的。原创 2023-11-06 11:12:45 · 166 阅读 · 0 评论 -
C++初阶-类和对象(中)2
此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义。原创 2023-11-05 20:37:12 · 203 阅读 · 0 评论 -
C++初阶-类和对象(中)1
对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?原创 2023-10-28 15:23:53 · 166 阅读 · 0 评论 -
C++初阶-类和对象(上)
/ 类体:由成员函数和成员变量组成// 一定要注意后面的分号class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::一般情况下,更期望采用第二种方式。原创 2023-10-18 16:05:52 · 252 阅读 · 0 评论