c++
Cry .
当你的能力配得上你的野心、梦想的时候,运气自然就会来了。
展开
-
数据结构之跳跃表
在以往的动态平衡数据结构中,我们学习过AVL tree, RB tree等等。二者的对元素的插入,删除,访问的时间复杂度都为对数时间。但是二者相对比较复杂,构建也比较麻烦(尤其是RB树,太难了~~)。有没有也存在一种动态平衡的数据结构,对元素的插入,删除和访问操作为对数时间尼?当然有! 那就是跳跃表。我们都知道链表的插入或者删除操作的时间复杂度为常数时间,但是其索引元素的时间复杂度便为线性时间...原创 2020-03-05 22:34:08 · 363 阅读 · 0 评论 -
Effective Modern C++ 之 引用折叠
前面我们介绍到,当函数模板的形参的型别为万能引用时,当传递给该函数模板的实参是个左值时,则推导出的形参的类型型别为左值引用,当传递给该函数模板的实参是个右值时,则推导出的形参的类型型别为右值引用。还有在实现完美转发时,当传递给调用者的实参是左值时,使用std::forward()被调者的形参的型别为左值引用,当传递给调用者的实参是右值时,使用std::forward()被调者的形参的型别为右值引用...原创 2020-02-12 14:52:47 · 135 阅读 · 0 评论 -
Effective Modern C++ 之 std::forward
std::forward与std::move的行为机制很类似。前面我们讲过std::move的实质是对传入的实参进行强制类型转换,最终转换为右值。从这里我们可以看出std::move实施的强制类型转换是无条件的。std::forward最长使用的场景是:函数模板以万能引用为新参,任何调用另一个函数。现在先让我们看一个简单的测试案例:void fun1(std::string& str...原创 2020-02-10 15:15:16 · 174 阅读 · 0 评论 -
Effective Modern C++ 之 std::move
移动语义可以使编译器使用移动操作来替换复制操作。移动构造函数和移动复制运算符具有控制对象移动的能力。std::move()可以实现移动语义,但是std::move()到底是怎么实现的呢?其底层原理是什么呢?首先我们先观察一下std::move()的源码:template<typename T>decltype(auto) move(T&& t)noexcep...原创 2020-02-10 11:26:57 · 175 阅读 · 0 评论 -
Effective Modern C++ 之 优先选用make_unique和make_shared
我们知道make_unique和make_shared可以创建unique_ptr和shared_ptr。make_unique和make_shared底层实现是将其形参向待创建对象的构造函数作了一次完美转发,从一个new运算符产生的裸指针出发,构造了一个unique_ptr或shared_ptr,然后返回创建的这个unique_ptr或者shared_ptr。new运算符也可以创建对象,那么ne...原创 2020-02-10 10:02:29 · 352 阅读 · 0 评论 -
程序员的自我修养——链接,装载与库(三)
目标文件从结构上讲,它是编译后的可执行文件格式,只是还没有经过链接的过程。其中可能有些符号或有些地址还没有被调整。其实它本身就是按照可执行文件格式存储的,只是与真正的可执行文件在结构上稍有不同。 可执行文件的格式分为windows下的PE和linux下的ELF。 目标文件是源代码编译后未进行链接的中间文件(windows下的.obj,linux下的.o),与可执行文件的内容和结构很...原创 2020-02-09 16:45:20 · 215 阅读 · 0 评论 -
Effective Modern C++ 之 shared_ptr
我们前面介绍了unique_ptr智能指针,它对它所指向的对象资源具有专属所有权。这个就直接导致unique_ptr是无法进行复制操作的。有没有一种智能指针对象资源不具有专属所有权,也就是它可以进行复制操作。当然有的。那就是shared_ptr智能指针。shared_ptr也是对裸指针进行包装的类。shared_ptr智能指针对它所指涉的对象资源具有共享所有权,也就是说指涉到该对象资源的所有的...原创 2020-02-09 11:10:43 · 124 阅读 · 0 评论 -
Effective Modern C++ 之 unique_ptr
首先我们应该知道为什么要使用智能指针。智能指针是对野指针的包装,它们的行为类似于被封装起来的裸指针。因此,避免了很多裸指针所带来的许多陷阱。直接使用裸指针可能会造成未定义行为,可能会产生悬垂指针等待。C++11中共有三种智能指针:unique_ptr,shared_ptr和weak_ptr。今天我们就先深层次了解一下unique_ptr吧。unique_ptr具有专属所有权的语义,因此它只能进...原创 2020-02-08 22:50:06 · 297 阅读 · 0 评论 -
Effective Modern C++ 之 constexpr
程序的执行分为编译阶段和运行阶段。为了让程序的执行时间最短,我们应该在编译阶段和运行阶段进行权衡。constexpr应用于对象时,对象具有const属性(加强版的const),其对象在编译阶段就已知。这些对象可能被存放在ROM里面(只读内存)。constexpr一般被使用在数组的尺寸规格,整型模板实参,枚举量的值和对齐规格中。 我们应该有效的区别const和constexpr。constexpr...原创 2020-02-08 10:11:52 · 105 阅读 · 0 评论 -
Effective Modern C++ noexcept
我们知道关于函数发射异常这件事,真正重要的信息是他会不会发射异常。在C++11中,无条件的为函数声明noexcept就是为了不会发射异常的函数而准备的。函数是否要加上如此声明,事关接口的设计。当我们知道函数不会发射异常但却没有为函数加上noexcept,这就是接口规格设计存在缺陷。为不会发射异常的函数加上noexcept,可以让编译器生成更好的代码(也就是说对代码会进行优化)。在带有noexc...原创 2020-02-07 11:04:30 · 139 阅读 · 0 评论 -
STL源码剖析之slist
我们都知道list的底层实现是双向链表,今天我们就讲一讲底层为单向链表的slist,其实现比较简单。listnode.htemplate<typename T>struct ListNode { T value; ListNode<T>* next;public: ListNode(const T _value) :valu...原创 2020-02-07 09:37:23 · 220 阅读 · 0 评论 -
Effective Modern C++ 之 override
我们都知道C++是面向对象的语言,而在C++中OOP的三大特性是:数据抽象,继承和多态。在实现多态中是通过虚函数来实现的,派生类中的虚函数的实现会改写基类中对应的虚函数的实现。而要改写的这个动作的完全实现必须满足一些要求: 基类中的函数必须是虚函数。 基类和派生类中的函数的签名必须完全相等。 基类和派生类中的函数的引用饰词(可以让左值和右值的对象的处理区分开来)必须完全相...原创 2020-02-06 15:53:52 · 142 阅读 · 0 评论 -
Effective Modern C++ 之 删除函数与private未定义的函数
如果我们想阻止对象去调用函数,我们便可以把该函数声明成private,并且不去定义它。但是这样真的可以阻止客户对象区调用它吗?其实是不行的,虽然我们把函数声明为private,客户对象只是不能直接的去进行调用,但是却可以间接的进行调用(通过使用接口函数对其进行调用便可)。有什么方法可以让一个函数永远不会使用呢? 当然有啊~~ C++11中,有更好的途径来达成效果上相同的结果:使用“=del...原创 2020-02-06 11:58:59 · 134 阅读 · 0 评论 -
Effective Modern C++ 之 枚举型别
枚举型别也是类,是一种特殊的类(里面只允许存在整型常量的类)。枚举量都是整型常量。枚举型别分为限定作用的和不限定作用域的。顾名思义,不限定作用域的表示枚举量在任何地方都可以使用,而限定作用域的只能在其作用域里使用。因此,不限定作用域的枚举类会泄漏名字,造成名字的空间污染。 限定作用域的枚举类的枚举量是更强型别的,也就是说其枚举量不发生隐式的类型转换。而不限定作用域的枚举类的枚举量却可...原创 2020-02-05 22:11:44 · 122 阅读 · 0 评论 -
Effective Modern C++ 之 优先选用nullptr,而非或NULL
我们知道C++的基本观点是0的型别是int,而非指针。0和NULL(C里面的空指针)在C++都不具备指针型别。C++中我们使用nullptr一般表示空指针(其实nullptr不具备任何的型别,它是一种任意类型的指针)。nullptr还可以提高代码的清晰性,nullptr不会造成0和NULL造成的代码的二义性问题,因此C++中表示空指针时,相对于0和NULL,我们应该优先选用nullptr。...原创 2020-02-05 10:56:29 · 112 阅读 · 0 评论 -
STL源码剖析之stack
大家知道在程序设计里面,哪种数据结构使用率最高呢?答案是:栈。为什么呢?因为程序设计里面会存在的大量的函数调用,而函数的调用实质是使用了栈。今天就让我们来了解一下栈的底层实现吧~栈是一种先进后出的数据结构,它只有一个出口,因此它允许在最顶端增加元素和删除元素。所以栈是不存在迭代器的,也就是说栈不存在遍历行为啦。由于栈以底部的某个容器完成其所有的工作,所以我们可以把栈理解为“修改某数据结构的...原创 2020-02-02 20:04:30 · 230 阅读 · 0 评论 -
Effective Modern C++ 之 () 和{}的区别
小括号和大括号都可以给对象进行初始化。但是大括号的范围更广,效率更高。大括号禁止内建型别之间隐式窄化型别转化,也就是对对象初始化时,不会发生隐式的型别转化。大括号还可以对最令人苦恼之解析语法免疫。当我们对对象进行默认构造时,有时也不注意,我们会写成声明一个函数,这个是很危险的。但是,使用大括号进行初始化,就不会出现此类的问题。前面我们讲过使用大括号的auto型别推导,会推导成std::in...原创 2020-02-02 11:20:07 · 234 阅读 · 0 评论 -
STL源码剖析之deque
前面我们讲过,vector的底层是连续线性存储的,其是动态的分配内存空间的,而vector只支持尾端的插入和删除操作。有没有一种数据结构首尾都支持插入和删除的的操作呢?当然有啊~ 就是deque(双端队列)。deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端进行插入和删除的操作。deque和vector的最大差异在于deque允许于常数时间内对首尾两端进行插入和删除,还...原创 2020-02-02 09:46:41 · 175 阅读 · 0 评论 -
STL源码剖析之list
先前我们讲过vector是连续线性存储的,其访问元素的时间复杂度是,但是插入或者删除元素的时间复杂度是,所以其插入或者删除的效率比价低。有什么容器的插入或者删除的效率是常数时间呢? 有啊~~ 就是list。list的每次插入或者删除元素时,配置或者释放一个内存空间,其时间复杂度永远是。现在就让我们来了解一下list的底层实现吧。首先让我们来看一下list的节点。template<t...原创 2020-02-01 18:34:00 · 161 阅读 · 0 评论 -
Effective Modern C++ 之 auto的使用
前面的总结中,我们对auto进行了总结。我们了解到auto的类型推导实质是模板类型推导。现在我们来讲解一下auto的使用,有人会觉得auto的使用很简单的,其实不然,auto的使用中存在很多的陷阱的。 我们都知道在栈上分配的内存空间,必须进行初始化,不然会导致未定义的行为。有时候我们显示的声明对象的型别,可能会忘记初始化,因此便会出现未定义的行为。有什么办法呢? 有啊~~ 使用auto啊,通...原创 2020-01-30 23:03:46 · 177 阅读 · 0 评论 -
STL源码剖析之vector
容器vector的底层实现是由数组(连续的线性空间)实现的,veector是动态分配内存空间的,也就是说,随着元素的加入,它的内部机制会自行扩充空间以便于容纳新元素。因此vector的底层实现技术的核心是对大小的控制以及重新分配内存空间时对数据的移动效率。因为vector的底层实现是由数组来实现的,所以vector的迭代器就是原生指针。vector的简单实现如下:我把迭代器的内嵌的相应的型别封...原创 2020-01-30 10:50:38 · 167 阅读 · 0 评论 -
设计模式之单例模式
以下是我摘自维基百科,我觉得总结的特别好:单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式的一种。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配...原创 2020-01-22 12:41:15 · 134 阅读 · 0 评论 -
Effective Modern C++ 之 auto和decltype的型别推导
首先我们应该知道auto型别推导的实质就是模板类型推导,在型别推导过程中,把auto替换成T就可以(可以看看我之前写的模板类型推导)。是不是所有的auto都可以与模板的类型推导建立一一映射关系呢?当然不是啦!!auto型别推导中有一条特殊的推导规则。接下来就让我们我们讲一讲啦~~当用auto声明变量,并且该变量用大括号进行初始化时,其推导所得的型别是std::initializer_list&...原创 2020-01-21 10:32:54 · 136 阅读 · 0 评论 -
Effective Modern C++ 之 模板型别推导
模板的型别推到是C++最广泛的特性之一。现在让我们先简单看一个函数模板推导。template<typename T>void fun(PRAMTYPE pram);fun(_parm);这时我们会问T的类型是什么呢?PRAMTYPE的类型又是什么呢?_pram和T的类型,PRAMTYPE的类型存在什么关联呢?这时候就要分三种情况去讨论。PRAMTYPE具有指针或...原创 2020-01-14 23:08:00 · 186 阅读 · 0 评论 -
设计模式之工厂模式
今天我们来了解一下工厂模式吧~~以下是我摘自维基百科:工厂方法模式(英语:Factory method pattern)是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。什么意思呢?其实...原创 2020-01-03 10:59:03 · 107 阅读 · 0 评论 -
Find—Union(并查集)
有时候我们想要查询两个元素是否在同一个集合中或者想要合并两个集合,怎么办呢?有人会说:“用二叉树,AVL树,链表,布隆过滤器也许啊”等等。但是“杀鸡怎么可以用牛刀呢?更何况,效率可能还不高”那怎么解决呢? 用并查集可以完美的解决这个问题哦~~并查集是一个树状结构(对集合进行抽象的描述),用于处理一些不相交的集合(Disjoint Sets)的合并及查询问题。既然是树状结构,那每个节点肯定有其p...原创 2019-12-24 17:33:39 · 453 阅读 · 1 评论 -
C++之最小生成树算法(Prim)
在讲述Prim算法之前让我们先了解一下什么是最小生成树?以下是我摘自维基百科:最小生成树是一副连通加权无向图中一棵权值最小的生成树。在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即 {(u,v)\in E}(u,v)\in E),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集 且 (V, T) 为树,使得{w(T)={(...原创 2019-12-06 23:24:37 · 3377 阅读 · 0 评论 -
C++之最短路径算法(Dijkstra)
前面我们讲过权值为1时的最短路径的算法,也就是无权最短路径算法。但是如果时赋权图呢?我们该怎么办呢?解决单源(只有一个开始顶点)最短路径的算法为Dijkstra,而该算法的核心思想是贪婪算法。贪婪算法的思想简单来讲就是“目光短浅,眼前利益为主”,也就是分阶段求解问题,在每个阶段去寻找最优解。Dijkstra算法也是按阶段进行,在每个阶段选择离开始顶点最近的顶点(未访问),我们对每一个顶点(v...原创 2019-12-04 22:58:56 · 2539 阅读 · 0 评论 -
C++之最短路径算法(无权最短路径)
无权的最短路径的算法与广度优先搜索算法的实质是一样的。层层递推(每次路径值加一),层层被赋值,就像树的层序遍历。#include<iostream>#include<queue>#include<list>#include<memory>#include<vector>const size_t InFinity...原创 2019-11-26 21:17:56 · 899 阅读 · 0 评论 -
C++之深度优先搜索(DFS)
深度优先搜索算法遵循的搜索策略是尽可能“一条路走到黑”的搜索一个图。在深度搜索策略中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去。到没有顶点的边可以探测时,就回溯到前一个顶点,并重复以上过程。整个过程反复进行,直到所有的顶点都已被发现时为止。发现没?深度优先搜索算法是树的前序遍历的演变。算法的时间复杂度也是。 #include<iostre...原创 2019-11-19 21:00:20 · 515 阅读 · 0 评论 -
C++之广度优先搜索(BFS)
对树的遍历中,我们学过前序遍历,中序遍历,后序遍历。图是一种特殊的树,因此图的遍历就是由树的遍历而演变过来的。图有两中遍历的算法:广度优先搜索(BFS) 深度优先搜索(DFS)。今天我们先来学习一下广度优先搜索吧。学习深度优先搜索,请参考我的另一篇博客。广度优先算法的思想:给定源顶点s,然后搜索与s所邻接的顶点,然后再搜索s邻接顶点的邻接顶点,以次下去,直到搜索到所有的顶点。该思想就是树的层序...原创 2019-11-19 16:54:50 · 1033 阅读 · 0 评论 -
C++之拓扑排序
拓扑排序是图论中的一种常见排序的算法(当且仅当为有向图成立)。图论中顶点与顶点存在依赖关系,比如A—>B,解释为A与B存在依赖关系,A在B之前先发生。有时候我们想了解图中各顶点的发生顺序,拓扑排序便解决了此问题。拓扑排序的思想:我们检查所有顶点的入度,当该顶点的入度为0时,便将其压入队列中(为什么要用队列呢?因为队列具有先进先出的思想啊~),当队列不为空时,便删除其首元素,然后降低与其...原创 2019-11-17 19:02:50 · 761 阅读 · 0 评论 -
C++之快速排序
快速排序顾名思义是目前实践中最快的排序算法。如归并排序算法一样,快速排序算法也使用了分治的递归思想。快速排序的思想:选取枢纽元,同过枢纽元对数据进行分割,左边的数据小于枢纽元,右边的数据大于枢纽元。然后利用分治递归的思想,对左右数据进行分割,最后进行合并。枢纽元的选取对快速排序非常重要!!怎么选取枢纽元呢?有的人会说选第一个或者最后一个数据。可以这样选择。但是如果出现第一个数据或者最后一个数据...原创 2019-11-10 20:10:07 · 194 阅读 · 0 评论 -
HashTable C++ 实现之开放定址法
解决HashTable的冲突的第二种方法叫做开放定址法。开放定址法不需要使用指针,不需要特意的动态分配空间。当发生冲突时此算法会选择其他的空的单元。因此核心问题来了,怎么有效的选择空的单元呢?算法一:线性探测法 F(i)=i(i为寻址次数)原理:对HashTable进行插入时,出现Index相同时,我们在原始Index的基础上加F(i)。但是问题又来了,因为F(i)为线性函数,它会连续的In...原创 2019-11-04 23:44:06 · 453 阅读 · 0 评论 -
HashTable C++实现之分离链接法
HashTable是以常数时间进行进行插入,删除和查找的数据结构。其查找原理是:通过哈希函数Hash(),进行哈希得到value,value为哈希表的下表。怎么能得到均衡的value呢?Hash()(哈希一般是通过字符串映射到键值)Index HashTable::Hash_one(const ElemType& key, const size_t tablesize)cons...原创 2019-11-03 18:48:57 · 253 阅读 · 0 评论 -
C++ 优先队列的实现(大堆)
优先队列是一种支持插入新元素和删除最大元素的数据结构。它和我们之前学过的队列和栈很相似,优先队列是队列和栈的拓展。而堆是支持优先队列的操作的一种数据结构。实现堆的方法有好几种,第一:我们可以使用链表,以O(1)时间插入,以O(N)时间删除。第二:使用二叉树,二叉树的所有操作需要,但是插入和删除操作却会导致二叉树严重的不平衡,那AVL树呢?插入和删除的操作需要,而且不会破坏树的平衡性,可以是可以,但...原创 2019-10-13 21:43:16 · 1402 阅读 · 0 评论 -
虚函数和纯虚函数的区别
原地址:https://blog.csdn.net/hackbuteer1/article/details/7558868关于虚函数和纯虚函数总结的特别好,所以转一下哦~~**首先:强调一个概念定义一个函数为虚函数,不代表函数为不被实现的函数。定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。定义一个函数为纯虚函数,才代表函数没有被实现。定义纯虚函数是为了实现一个接口,起到一个...转载 2019-03-09 15:41:20 · 160 阅读 · 0 评论 -
迭代器做函数模板的形参
****原创 2019-03-11 16:59:03 · 598 阅读 · 0 评论 -
C++转型操作符
cccc原创 2019-03-16 15:40:52 · 143 阅读 · 0 评论 -
c++ 临时对象
所谓临时对象,就是一种匿名对象,它的出现不在程序的预期之下(不会出现在你的源代码中),并且它不会产生在heap里。那么临时对象是怎么产生的呢? 传递某对象给一个函数,而其类型与它即将绑定上去的参数类型不同的时候。 值传递操作。 现在让我们仔细分析一下吧。1.传递某对象给一个函数,而其类型与它即将绑定上去的参数类型不同的时候。void Print(const std::...原创 2019-05-17 10:24:48 · 189 阅读 · 0 评论