
C++
文章平均质量分 91
C++的知识
dbln
这个作者很懒,什么都没留下…
展开
-
C++之类型转换
如果父类的指针(或引用)指向的是一个父类对象,那么将其转换为子类的指针(或引用)是不安全,会存在越界的风险,因为转换后可能会访问子类的资源,而这些资源是父类对象没有的。static_cast 用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。而*p指向的是内存中的a,内存中的a被修改了。而我们在监视窗口看到的是内存中的 “a”。如果父类的指针(或引用)指向的是一个子类对象,那么将其转换为子类的指针(或引用)则是安全的。原创 2023-10-31 08:00:00 · 1048 阅读 · 8 评论 -
C++之特殊类的设计
缺点:1、一个程序中有多个单例,并且有先后创建初始化顺序要求时,饿汉模式无法控制(无法控制初始化顺序)。2、如果饿汉单例类,初始化时任务多,会影响程序的启动速度。一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。优点: 1、一个程序中有多个单例,并且有先后创建初始化顺序要求时,懒汉模式可以控制(能够控制初始化顺序)。1、一般情况下,单例对象不需要释放。2、提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建或析构。原创 2023-10-28 08:00:00 · 934 阅读 · 7 评论 -
C++之智能指针
智能指针实例化的对象在其生命周期结束时,默认是以delete的方式将空间释放,这是不太合适的,因为我们并不是只是以new的方式申请到的内存空间,也可能是以new[]的方式申请到多个空间,或是一个文件指针。使用了智能指针后,在Func中,即使div的调用抛出了异常,直接跳到了catch位置,sp1和sp2的空间也能够释放。所谓智能指针,它的本质就需要像一个指针一样去使用。_next 和 _prev 是 weak_ptr 时,它不参与资源的释放管理,但是可以访问和修改资源,且不增加计数,不存在循环引用的问题。原创 2023-10-25 08:00:00 · 1041 阅读 · 6 评论 -
C++11(lambda表达式)
我们是不是觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些给我们带来了极大的不便。3、实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。如下面我们定义了一个商品的类,然后我们要分别根据价格的升序和降序进行排序,那么我们最先想到的方法就是使用仿函数来帮助我们排序。原创 2023-10-16 08:00:00 · 860 阅读 · 5 评论 -
C++之异常
如果有匹配的,则处理。实际使用中,人们一般都会自定义自己的异常体系进行规范的异常管理,因为在一个项目中,不同的负责组或者负责人可能会抛出不同的异常,如果没有规范,负责最外层捕获异常的人就需要捕获大家抛出的各种类型的异常。3、抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。1、异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。原创 2023-10-22 08:00:00 · 1340 阅读 · 7 评论 -
C++11(包装器)
std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。注:arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示a1的参数,它们占据了传递给a1的参数的“位置”。如此丰富的类型,可能会导致模板的效率低下。上面的模板中,即使T的类型保持不变,但是F的类型可能是函数指针,函数对象(仿函数对象),也有可能是lamber表达式对象。原创 2023-10-19 08:00:00 · 919 阅读 · 5 评论 -
C++11(列表初始化,声明,范围for)
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。而在C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。关键字decltype将变量的类型声明为表达式指定的类型,即:根据表达式的实际类型推演出定义变量时所用的类型。范围for本质上是迭代器,在代码编译的时候,编译器会自动将范围for替换为迭代器的形式去遍历。原创 2023-10-01 08:00:00 · 1060 阅读 · 3 评论 -
C++11(左值(引用),右值(引用),移动语义,完美转发)
但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值。右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,即:t 既能引用左值,也能引用右值。原创 2023-10-08 08:00:00 · 971 阅读 · 4 评论 -
C++11(新的类功能,可变参数模板,empalce函数)
emplace_back 的插入则是既可以像 push_back那样用,也可以用参数包的形式,依次取出参数,第一次取出的参数去初始化 pair的first,第二次取出的参数去初始化 pair 的 second。如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。原创 2023-10-10 17:12:21 · 836 阅读 · 6 评论 -
unordered_set和unordered_map的封装
在C++11中,STL提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,但是效率却提高了。unordered系列的关联式容器之所以效率比较高,是因为unordered系列的关联式容器的底层使用了我们前面所讲到的哈希结构。那么今天我们就来看看 unordered_set和unordered_map这两个容器的使用及其实现。原创 2023-09-22 08:00:00 · 702 阅读 · 5 评论 -
位图与布隆过滤器
我们先来看一道题。题目描述:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。思路一:遍历拿到这道题,我们最先想到的方法就是直接遍历了。从头到尾遍历一遍,一定可以判断出一个数是否在其中。时间复杂度为 o(N)。思路二:排序+二分查找时间复杂度为 排序 o(N*logN) + 二分查找 o(logN)。思路三:位图上面两种方法很常用,但是在面对40亿个数据时,无论是空间复杂度还是时间复杂度都很大。那么有没有比它们更加优秀的方法呢?原创 2023-09-26 08:00:00 · 680 阅读 · 2 评论 -
AVLTree——高度平衡二叉搜索树
3、更新后,parent->bf == 0,说明parent插入前的平衡因子是1 or -1,说明左右子树一边高一边低,插入后两边一样高,插入填上了矮的那边,parent所在子树高度不变,不需要往上继续更新。4、更新后,parent->bf == 2 or -2,说明parent插入之前的平衡因子是1 or -1,已是平衡临界值,插入变成 2 or -2,打破平衡,parent所在子树需要旋转处理。2、parent成为subR的左子树,更新parent的父亲为subR,而subR的左为parent。原创 2023-09-07 08:00:00 · 695 阅读 · 5 评论 -
哈希及哈希表的实现
比如三中的情况,插入2之后,现在需要插入元素32,先通过哈希函数计算哈希地址为2,因此32理论上应该插在该位置,但是该位置已经放了值为2的元素,即发生哈希冲突。桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容。搜索元素:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。当有这些数据时:1,4,5,6,7,9。原创 2023-09-18 08:00:00 · 1165 阅读 · 4 评论 -
map和set的封装
在标准库中map和set的底层结构是使用红黑树来实现的。但是,map是key,value类型的容器,set是key类型的容器,那我们是不是要分别写一个 kv类型的红黑树和key类型的红黑树去分别封装呢?还是说可以使用同一棵树去实现呢?下面我们从标准库源码去看一看它是怎么封装的。原创 2023-09-14 08:00:00 · 693 阅读 · 7 评论 -
list的模拟实现
list 无法直接通过++或者--来直接访问一个位置的前一个位置或者后一个位置,那么我们可以通过运算符重载来帮助我们来找到一个结点的前一个位置或者下一个位置。但是对于list而言,它的空间是不连续的,我们无法直接通过++或者--来直接访问一个位置的前一个位置或者后一个位置,所以使用指针来实现list的迭代器是不可能的了。我们需要进行一些特殊处理。对于vector和string类而言,物理空间是连续的,通过++或者--,我们就可以访问一个位置的前一个位置或者后一个位置,所以我们可以用原生的指针来实现迭代器。原创 2023-08-14 08:00:00 · 626 阅读 · 5 评论 -
数据结构——红黑树
1、如果u结点不存在,则cur一定是新增结点,因为如果cur不是新增结点:则cur和p一定有一个节点时黑色,就不满足每条路径都有相同的黑色结点的性质。首先,如果我们插入黑色结点,将会违背性质4,因为某条路径上一定会多出一个黑色结点,这样就需要去调节所有路径的黑色结点,非常麻烦。在一起的红色节点,此时需要对红黑树分情况来讨论:(cur为当前结点,p为父亲结点,g为祖父结点,u为叔叔结点)。2、如果u结点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色,在其子树调整过程中变为了红色。原创 2023-09-10 08:00:00 · 658 阅读 · 3 评论 -
二叉搜索树
从上面我们知道如果删除的结点是叶子结点的话,我们只需要直接删除它即可,那么我们有没有什么办法可以将要删除的结点调整到叶子节点的位置呢?即:要查找的值小于当前根节点的值,就去左子树找,要查找的值大于当前根节点的值,就去右子树找。然后就是左子树为空,或者右子树为空的情况,我们只需要在寻找删除结点时记录一下它的父亲结点,然后将删除结点的子节点连接给父亲结点即可。插入的寻找思路和查找一样:要插入的值小于当前根节点的值,就去左子树插入,要查找的值大于当前根节点的值,就去右子树插入,直到空位置位置。原创 2023-09-04 08:00:00 · 653 阅读 · 5 评论 -
C++之模板进阶
1、模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。2、增强了代码的灵活性。1、模板会导致代码膨胀问题,也会导致编译时间变长。2、出现模板编译错误时,错误信息非常凌乱,不易定位错误。原创 2023-08-28 08:30:00 · 667 阅读 · 5 评论 -
C++之多态
多态作为C++三大特性之一,在C++中具有十分重要的作用。那么今天我们就来讲一讲多态。两个函数分别在基类和派生类的作用域。函数名相同。两个基类和派生类的同名函数不构成重写就是重定义。原创 2023-09-02 08:00:00 · 643 阅读 · 4 评论 -
C++之继承
从内存的结构看去,我们发现指针指向的内容为它到取到 _a 的位置的偏移量,这样我们就可以找到共用的_a。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。在继承的情况中,不需要显示调用父类的析构函数,编译器自动调用,先析构子类,再析构父类。从上面的图中我们就可以看出在D中A的成员_a有两份,有数据冗余的问题。子类自己的成员,跟类和对象一样。原创 2023-08-31 08:00:00 · 651 阅读 · 6 评论 -
stack和queue的模拟实现
简单来说:从前面的学习中,我们知道栈和队列是具有特殊特性的数组或链表,即他们都是通过数组或者链表来实现的,因此我们可以直接使用 vector 或者 list 来帮助我们进行适配,从而实现 stack 和 queue。(他们都是使用容器转换出来的)原创 2023-08-19 08:00:00 · 611 阅读 · 1 评论 -
priority_queue的模拟实现
3、 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。4、 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。2、 优先级队列类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。1、 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。front():返回容器中第一个元素的引用。原创 2023-08-23 08:00:00 · 621 阅读 · 4 评论 -
vector的模拟实现
这时如果我们再插入数据 ,那么就会发生扩容,也就是会重新开辟空间,而重新开辟空间后pos的位置就没有指向新空间的pos的位置了,pos成为了一个野指针。举个例子:如果vector里的数据类型是 vector<int>,那么在这memcpy就是一个浅拷贝,虽然vector<vector<int>>有自己的空间,但是里面的数据vector<int>却没有自己的空间,memcpy后,两个对象的vector<int>会指向同一块空间。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。原创 2023-08-08 08:00:00 · 638 阅读 · 5 评论 -
C++学习总结——内存管理和模板初阶
说明:第一个是直接申请一个int,第二个是申请一个int并且初始化为5,第三个是申请5个int的数组,第四个是申请5个int的数组并且初始化。对于内置类型,new / delete与malloc / free 的作用完全相同,都是在堆上申请空间,只有用法的区别。函数模板概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。5、 malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需。如何实现一个通用的交换函数呢?原创 2023-07-24 08:00:00 · 730 阅读 · 0 评论 -
C++学习总结——类与对象(二)
静态成员函数不可以调用非静态成员(无this指针): 不需要使用对象去调用,可以直接调用。非静态成员可以调用静态的成员函数(因为静态成员属于整个类)原创 2023-07-26 22:13:04 · 655 阅读 · 4 评论 -
string的模拟实现
那是因为通过查阅标准库我们发现swap函数也是一个string类中提供了的函数,因此我们不仅简便了拷贝构造函数的写法,还又完成了一个函数的实现。上面的这种实现方法是比较常见的一种写法,但是我们还有一种更加简便的写法。我们可以通过已经实现了的构造函数传一个常量字符串,即下面的 str . _str,来创建一个l临时对象,然后将这个临时对象的成员与自己交换,这样也完成了拷贝构造。注:蓝色的swap函数是我们自己写的,而黄色的,前面写明了域的swap函数是C++库中的一个现成的函数。这就是我们所说的浅拷贝,原创 2023-08-01 08:00:00 · 689 阅读 · 3 评论 -
C++学习总结——初识C++
解释:调用Add函数时,会在堆区建立栈帧,出了Add函数的作用域,那个栈帧就销毁了,c所在的空间就返还给操作系统了。该函数是返回引用,只是返回了一个c所在空间的别名,即ret接收到的是和C共用C所在的空间。以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直 接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。原创 2023-05-10 22:59:21 · 733 阅读 · 1 评论 -
C++学习总结——类和对象(一)
成员变量。原创 2023-06-10 20:17:58 · 608 阅读 · 0 评论