
C++
文章平均质量分 95
C++相关知识
iiiii空
千里之行,始于足下。
展开
-
C++ 11 --- 中
如果我们不写,编译器会自动生成一份,而对于它们俩也不例外,但是,以前的默认成员函数,只要我们不写,编译器就会自动生成一份,例如,我们不写构造,那么编译器就会自动生成一份构造,然而,移动构造和移动赋值自动生成的条件却更为苛刻。也就是说,看似移动语义的自动生成条件很苛刻,但是如果一个类没有资源需要清理,那么就可以满足移动语义的自动生成条件,反之,如果有资源需要被清理,那么移动语义不会被自动生成。我们无法直接获取参数包 args 中的每个参数的, 只能通过展开参数包的方式来获取参数包中的每个参数。原创 2024-05-17 17:07:02 · 941 阅读 · 0 评论 -
C++11 --- 上
列表初始化对于内置类型来说,用的相对较少;但是对于自定义类型,特别是 STL 的容器,还是很有意义的。简单的理解:可以被取地址的对象,都被称之为左值;反之,不能被取地址的对象,称之为右值。在C++中,左值(L-value)是可以被标识符引用的表达式或对象,它具有持久性并且可以被修改。通常,变量、对象、数组元素和函数返回的左值引用都被认为是左值。左值可以取地址并且可以对它赋值。左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。原创 2024-05-17 16:52:09 · 928 阅读 · 0 评论 -
布隆过滤器
布隆过滤器优点:增加和查询元素的时间复杂度为:O(K),(K为哈希函数的个数,一般比较小),与数据量大小无关;哈希函数相互之间没有关系,方便硬件并行运算;布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势;在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势;数据量很大时,布隆过滤器可以表示全集,其他数据结构不能;使用同一组散列函数的布隆过滤器可以进行交、并、差运算。原创 2024-05-16 21:51:34 · 1025 阅读 · 0 评论 -
位图的实现和应用
对于无符号整型来讲,其范围是0 至 2^32-1,那么我们也就需要2^32个位即可,也就是 2^29 个字节,而我们知道 2^30 是1GB,那么 2^29 个字节也就是512MB,相较于上面的思路,节省了很大的空间。它将每一个位(0 或 1)映射到一个特定的索引位置,如果位为1,那么说明映射到这个位的Key是存在的,反之如果位为0,那么说明这个Key是不存在的。与上面的思路一致,同样用两个位图(去重),将文件的数据 set 到两个位图中,遍历两个位图,如果相同位置的位 == 1,则是交集。原创 2024-05-16 21:38:06 · 973 阅读 · 0 评论 -
哈希表的理解和实现
在闭散列中实现哈希表时,我们所用的哈希表的初始大小为10,且后续扩容是以2倍的方式进行的,但我们在直接说过,由于除留余数法存在哈希冲突,故为了减少哈希冲突,人们发现,如果表的大小为一个素数,就会减小哈希冲突的可能。具体的操作是,如果哈希表中的某个槽位已经被占用,则线性探测会依次检查下一个槽位,直到找到一个空闲的槽位,然后将元素存储在该位置。为了克服线性探测的缺点,还有其他的解决冲突方法,如二次探测等,二次探测,缓解线性探测的 "踩踏" ,在实际运用中,可以根据具体的场景和需求选择适合的解决方案。原创 2024-05-15 21:30:36 · 1130 阅读 · 0 评论 -
红黑树的理解和简单实现
因为如果新增节点是黑色,那么一定会违反红黑树的规则,反之,如果是红色,则可能插入之后不违反任何规则,因此我们将新增节点设置为红色。g --- grandfather(祖父节点 ), p --- parent(父节点) ,u --- uncle(叔叔节点),c --- cur(新增节点)。,继续判断,如果符合前面的条件(p、u为红)继续向上更新,最后为了避免不同的情况,将根节点的颜色更新为黑即可。思路: 只要某节点是红色的,那么只要判断它的父亲即可,如果父亲是红色的,那么就不是红黑树,反之,则符合红黑树。原创 2024-05-12 17:35:45 · 1169 阅读 · 0 评论 -
AVL 树的理解和简单实现
这样可以提供更好的性能和效率。旋转处理的情况分为四种:左单旋、右单旋、左右双旋、右左双旋;旋转的原则:旋转后仍是一颗AVL树;旋转的目的:左右均衡,降低整棵AVL树的高度。原创 2024-05-12 17:17:34 · 743 阅读 · 0 评论 -
map 和 set 的介绍和简单使用
原因是,operator[] 通常用于直接访问容器中特定键 (Key) 对应的值 (Value),而对于 std::multimap 这种允许一个键关联多个值的容器来说,使用 operator[] 并不明确应该返回哪个值 (Value),此时的 operator[] 的行为就变得模糊不清。最后,(*((this->insert(std::make_pair(k, mapped_type()))).first)).second 取得插入的键值对中值 (Value) 所在的部分,即当前插入的键所对应的值。原创 2024-05-12 17:17:58 · 637 阅读 · 1 评论 -
二叉树进阶 --- 中
相信前两个过程是没有困难的,最后一步可能不好实现,但是当我们经过了前两个过程,我们发现被删除节点变成了我们找到的"合适节点",而且这个"合适节点"很有特征,如果它是左子树的最大值,那么它一定不会有右子树,反之,如果他是右子树的最小节点,那么它一定不会有左子树。由于 root 是4的右孩子的引用,且 root 的右孩子为空,那么root = root->_left,就可以将4的右孩子由6变更为5,我们在删除6即可,因此我们需要提前保存6节点,当指向变更之后,delete 6。找到合适位置,创建节点并连接。原创 2024-05-11 21:29:11 · 920 阅读 · 0 评论 -
二叉树进阶 --- 上
假如现在我们要删除4所在的节点,可以发现,4所在的节点有两个孩子,因此无法托孤,那么我们需要采用替代法删除,替代法删除就是在左子树或者右子树找一个"合适节点",将4所在的节点的key进行覆盖,将删除4所在的节点转化为删除我们找的这个"合适节点"。同时,我们发现,最后进行托孤法删除时,我们也进行了判断,这样的原因是因为这个"合适节点"既可能是父节点的左孩子,也可能是父节点的右孩子,因此必须判断。如果被删除节点是父亲节点的左孩子,那么在这里就是父亲节点的左孩子指向被删除节点的非空节点。原创 2024-05-11 21:29:06 · 936 阅读 · 0 评论 -
C++ 多态的相关问题
赋值运算符重载可以是虚函数,因为调用赋值的两个对象是已经存在的对象,既然已经存在的对象,如果有虚函数,那么虚表的指针是被初始化过了的,也就是说赋值运算符重载可以进入虚表,虽然赋值运算重载可以是虚函数,但是赋值运算符重载实现多态是没有实际价值的。首先,派生类 B 继承 A类,会将B类的方法继承下来,但注意,继承是派生类有访问基类方法的权限,而并不是说基类的方法在派生类中也有一份,继承后的基类方法依旧属于基类;清楚了这个问题 ,接下来就简单了,之前说过,初始化列表的初始化顺序是由继承的先后顺序决定的。原创 2024-05-10 17:46:06 · 843 阅读 · 0 评论 -
C++ 多态 - 下
可以看到,无论以上面的任一方式,最后调用的 Func1()是一定的 (都是 Derive::Func1 ),只不过不同的调用可能会做一些封装 (为了处理不同的情况)。具体解释:首先:这里存在很多编译器的行为,比如我们打印的地址事实上并不是函数 (Derive::Func1) 的真实地址(即函数的第一条指令的地址),而是一个跳转指令 (例如这里的 jmp 指令 ) 的地址,跳转指令会跳转至函数真正的执行语句。但是我们依然可以理解为虚表中存储的是虚函数的地址!原创 2024-05-10 15:15:28 · 1340 阅读 · 0 评论 -
C++继承 - 下
很多人说 C++ 语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,也不建议设计出菱形继承,否则在代码维护以及性能上都有问题;多继承可以认为是 C++ 的缺陷之一,很多后来的oo(object--oriented) 语言都没有多继承,如 Jave;继承和组合:public 继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象;组合是一种 has-a 的关系。原创 2024-05-07 17:24:00 · 877 阅读 · 0 评论 -
list 的模拟实现
首先,在 string 和 vector 的模拟实现中,我们也使用过迭代器,但是它们两个的迭代器很特殊,因为它们存储的元素的地址是连续的,因此,它们的迭代器本质上就是原生指针,那 list 这里能不能也是原生指针呢?lt {1, 2, 3, 4},通过 it 拷贝构造得到 copy, copy {1, 2, 3, 4},copy 遍历一遍,每个元素 *= 2,即 copy {2, 4, 6, 8},那么此时 lt 是什么呢?可是我们发现一个问题,这咋遍历呢?这两个函数构成函数重载吗,答案,构成,为什么呢?原创 2024-05-05 19:30:50 · 878 阅读 · 0 评论 -
vector 的模拟实现
经过对比我们得知,造成这种问题的愿意就是因为发生扩容后,_start、_finish、_end_of_storage的值发生了改变 (因为是新空间),而pos还是在扩容之前的位置上,那么接下来的while循环也就没有意义了,进程崩溃也就可以理解了;但是对于某些类来说,编译器默认生成的拷贝构造是不符合需求的;原因是因为这是值传递,形参的改变不影响实参,因此在这里的 pos 依旧是失效的(当发生扩容时),因此我们得出的结论就是当插入了数据,不要再访问这个pos了,因为我们不能万无一失确保pos的有效性;原创 2024-05-04 17:24:30 · 990 阅读 · 0 评论 -
string 的模拟实现
字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)string str;// 此处用 cin 不会通过测试用例if(pos!else。原创 2024-05-04 14:44:52 · 912 阅读 · 0 评论 -
C++的IO流
1. 流在 C++ 中,流(stream)是一种用于输入和输出操作的抽象概念。它被用于实现数据的输入和输出,使程序能够与外部环境进行交互。C++流是指数据从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。C++ 提供了一个标准库(iostream),其中包含了用于输入和输出的流类。流类分为输入流(istream)和输出流(ostream)两种类型。输入流用于从外部读取数据到程序中,而输出流用于将程序的数据输出到外部。原创 2023-10-22 19:26:14 · 205 阅读 · 0 评论 -
C++的类型转换
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失2. 显式类型转换将所有情况混合在一起,代码不够清晰,不太规范。因此在C++中,提出了自己的类型转换,但由于C++要兼容C,因此以前的转换方式C++依旧支持,兼容C隐式类型转换和强制类型转换,但是C++期望的是不要再用C的那一套类型转换,期望你用规范的C++显示强制类型转换2. C++强制类型转换在C++标准中,为了规范类型转换,标准提出了四种命名的强制类型转换操作符。原创 2023-10-21 18:57:28 · 628 阅读 · 0 评论 -
C++特殊类设计
在单例模式中,类的构造函数被定义为私有的,这样外部无法直接创建实例。4. 静态内部类(Static Inner Class):利用静态内部类的特性,在外部类加载时不会创建实例,只有实际调用内部类时才会触发实例的创建。需要注意的是,在多线程环境下,需要考虑线程安全性,避免出现竞态条件和实例的重复创建。当不存在实例时,会在获取实例时创建一个新的实例,并在后续的调用中返回这个实例。5. 枚举类(Enum Class):使用枚举实现单例模式,枚举类型的实例是在类加载时完成初始化的,保证了线程安全性和实例唯一性。原创 2023-10-20 14:57:32 · 254 阅读 · 0 评论 -
C++智能指针
对于new A[5]来说,编译器是知道需要调用5次构造函数 + malloc的,而对于delete来说,也是确定的,一次析构 + 一次free,原创 2023-10-19 00:22:20 · 370 阅读 · 0 评论 -
C++异常
实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。原创 2023-10-15 21:07:40 · 767 阅读 · 0 评论 -
C++11 --- 下
列表初始化对于内置类型来说,用的相对较少;但是对于自定义类型,特别是标准库的容器,还是很有意义的。那么问题来了,那上面的代码是如何被支持的呢?原创 2023-10-14 19:02:11 · 447 阅读 · 0 评论 -
unordered_set && unordered_map 的封装
对于无符号整型来讲,其范围是0至2^32-1,那么我们也就需要2^32个位即可,也就是2^29个字节,而我们知道2^30是1GB,那么2^29个字节也就是512MB,相较于上面的思路,节省了很大的空间。我们发现,上面的映射关系可能不具备唯一性。也就是说,当我们去判断某一个值存不存在的时候,可能具有误判,假设上面没有"野牛"这个字符串,但是当我们去判断"野牛"存不存在的时候,如果此时"大熊猫"是存在的,那么我们也会得到"野牛"是存在的结果,而如果此时"大熊猫"也不存在,那么我们会得到"野牛"是一定不存在的。原创 2023-10-16 23:15:21 · 271 阅读 · 0 评论 -
set 和 map 的封装
这样可以提供更好的性能和效率。旋转处理的情况分为四种:左单旋、右单旋、左右双旋、右左双旋旋转的原则:旋转后仍是一颗AVL树旋转的目的:左右均衡,降低整棵AVL树的高度。原创 2023-10-02 17:55:25 · 275 阅读 · 0 评论 -
二叉树进阶 - 下
学习二叉搜索树的一些原因:1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构2.二叉搜索树的特性了解,有助于更好的理解map和set的特性。原创 2023-09-23 12:02:47 · 146 阅读 · 2 评论 -
C++ 多态 - 上
3.抽象类在虚函数的后面写上 = 0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。public:// 虚函数 = 0 ,被称之为纯虚函数,包含纯虚函数的类称之为抽象类(接口类)// 纯虚函数是可以不用实现的,对于animals类来说,由于它是抽象类,因此不会有实例化对象// 既然都不会有对象,自然也不会调用这个函数。原创 2023-09-10 16:26:58 · 168 阅读 · 0 评论 -
C++模板-进阶
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。原创 2023-08-15 19:30:02 · 97 阅读 · 0 评论 -
C++继承 - 上
1.2.3继承基类成员访问方式的变化class Apublic://将基类的构造函数私有化private::_a(a)protected:int _a;Xq::B b;编译报错,对于基类的构造函数是私有的情况,无法被继承;因为子类对象实例化,无法调用积累的构造函数;上面的做法是C++98的做法;而C++11的做法是:增加了一个关键字finalclass A final //final 意味着A类不可被继承public::_a(a)原创 2023-09-07 20:22:26 · 142 阅读 · 1 评论 -
List的理解和简单实现
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。3. list与非常相似:最主要的不同在于是单链表,只能朝前迭代,已让其更简单高效。4.与其他的序列式容器相比(arrayvectordeque)list通常在任意位置进行插入、移除元素的执行效率更好。5.与其他序列式容器相比,list和。原创 2023-08-14 12:19:45 · 179 阅读 · 0 评论 -
标准库中 vector 的使用
1. vector是表示可变大小数组的序列容器。2.就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。3.本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。原创 2023-08-08 21:38:37 · 74 阅读 · 0 评论 -
标准库中 string 的使用
字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)string str;//此处用cin不会通过测试用例if(pos!else。原创 2023-07-22 23:08:21 · 224 阅读 · 2 评论 -
C++模板-初阶
比如:当用int类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码,double也是如此;如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。a.重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数,工作量太大;函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型实例化函数的特定类型版本。,然后将实例化的类型放在中即可,类模板名字不是真正的类,而实例化的结果才是真正的类;原创 2023-07-20 17:13:28 · 96 阅读 · 0 评论 -
C++内存管理
其实对于内置类型来说,malloc和new没有什么太大的区别;真正有差异的时操作自定义类型;delete p2;return 0;答案是new 对于自定义类型,先在堆上申请空间,然后调用类的默认构造函数初始化;如果没有默认构造且没有主动初始化 ,编译报错;delete 先调用析构函数清理对象中资源,然后在释放空间;原创 2023-07-19 22:53:19 · 101 阅读 · 0 评论 -
类和对象-下
这是因为初始化列表的顺序是由成员属性声明顺序所决定的,在这个类中,_a2先声明,_a1后声明,也就是说在初始化列表当中先进行初始化的是_a2,此时_a1是一个随机值,然后再对_a1进行初始化,也就得到了我们所看到的结果;因为func()返回的时候会调用拷贝构造生成一个临时对象,这个临时对象又要进行拷贝构造target,编译器对这种连续的拷贝构造会进行优化处理,即一个表达式中,连续的拷贝构造 + 拷贝构造 ---> 优化成 一个拷贝构造;因此结论就是:初始化列表初始化成员属性的顺序是成员属性声明的顺序;原创 2023-07-18 22:05:40 · 95 阅读 · 1 评论 -
类和对象-中
编译器所生成的默认构造函数,将成员属性的类型分为两种类型,即自定义类型(struct、class)和内置类型(int、double、int*...);总结: C++我们不写构造函数,编译器就会默认生成一个构造函数,但是设计不好,没有对内置类型和自定义类型统一处理;构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦。原创 2023-07-15 12:08:51 · 102 阅读 · 1 评论 -
类和对象-上
经过对比,我们知道class默认成员(成员方法和成员属性)都是属于private的,无法在类外直接访问,而struct默认成员是属于public的,可以在类外直接访问。但是某些编译器会将this指针存于寄存器(ecx),因为频繁的访问this指针的话,某些编译器考虑到效率的问题,将this指针存于寄存器里面,用来提高效率;类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没。只计算成员属性的大小,并考虑内存对齐,成员方法不在类里面,而在公共代码区里;C是面向过程,关注的是过程;原创 2023-07-15 11:25:15 · 108 阅读 · 1 评论 -
C++基础语法
C++基础语法,及相关介绍原创 2023-07-14 20:33:10 · 154 阅读 · 1 评论 -
C++栈和队列
1.1. 介绍1. stack是一种容器适配器,专门用在具有后进先出(LIFO)操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶被压入和弹出。3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:empty:判空操作back:获取尾部元素操作。原创 2023-08-14 12:17:38 · 131 阅读 · 0 评论