C++ 进阶基础之十
大纲
- C++ 进阶基础之一、C++ 进阶基础之二、C++ 进阶基础之三
- C++ 进阶基础之四、C++ 进阶基础之五、C++ 进阶基础之六
- C++ 进阶基础之七、C++ 进阶基础之八、C++ 进阶基础之九
- C++ 进阶基础之十、C++ 进阶基础之十一
函数对象
函数对象的概念
尽管函数指针被广泛用于实现函数回调,但 C++ 还提供了一个重要的实现回调函数的方法,那就是函数对象。重载函数调用操作符 ()
的类,其对象常称为函数对象(Function Object),即它们是行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过 对象名 + (参数列表)
的方式使用一个类对象,如果没有上下文,完全可以把它看作一个函数对待,这是通过重载类的 ()
操作符来实现的。在 C++ 标准库中,函数对象被广泛地使用以获得弹性,并且标准库中的很多算法都可以使用函数对象或者函数来作为自定义的回调行为。值得一提的是,函数对象的别名是 仿函数
,或者是 伪函数
。
Kafka 入门教程之一
Linux 生产环境搭建 Kafka 集群
大纲
前言
本文将使用多台物理机器(至少三台)搭建 Kafka 集群,适用于 CentOS/Debian/Ubuntu 等发行版。
Zookeeper 集群搭建
本文的 Kafka 集群搭建依赖于 Zookeeper,因此生产环境需要将 Zookeeper 集群提前搭建起来。值得一提的是,从 Kafka 2.8.0
版本开始,Kafka 自身实现了 Raft
分布式一致性机制,这意味着 Kafka 集群是可以脱离 ZooKeeper 独立运行的。
集群规划
节点 | IP 地址 | 端口 | 版本号 |
---|---|---|---|
Zookeeper 节点 1 | 192.168.1.1 | 2181 | 3.4.10 |
Zookeeper 节点 2 | 192.168.1.2 | 2181 | 3.4.10 |
Zookeeper 节点 3 | 192.168.1.3 | 2181 | 3.4.10 |
Linux 单机搭建 Kafka 集群
大纲
前言
本文适用于在 Centos/Debian/Ubuntu 等 Linux 发行版系统上,使用单机搭建 Kafka 集群。
Zookeeper 集群搭建
本文的 Kafka 集群搭建依赖于 Zookeeper,因此需要将 Zookeeper 单机集群提前搭建起来。值得一提的是,从 Kafka 2.8.0
版本开始,Kafka 自身实现了 Raft
分布式一致性机制,这意味着 Kafka 集群是可以脱离 ZooKeeper 独立运行的。
集群规划
节点 | IP 地址 | 端口 | 版本号 |
---|---|---|---|
Zookeeper 节点 1 | 127.0.0.1 | 2181 | 3.4.10 |
Zookeeper 节点 2 | 127.0.0.1 | 2182 | 3.4.10 |
Zookeeper 节点 3 | 127.0.0.1 | 2183 | 3.4.10 |
C++ 进阶基础之九
大纲
- C++ 进阶基础之一、C++ 进阶基础之二、C++ 进阶基础之三
- C++ 进阶基础之四、C++ 进阶基础之五、C++ 进阶基础之六
- C++ 进阶基础之七、C++ 进阶基础之八、C++ 进阶基础之九
- C++ 进阶基础之十、C++ 进阶基础之十一
set 与 multiset 容器
set 与 multiset 容器的概念
set 容器的概念
set 是一个集合容器,其中所包含的元素是唯一的,集合中的元素会自动按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。set 的元素不像 map 那样可以同时拥有实值和键值,set 的元素即是键值又是实值。Set 不允许两个元素有相同的键,也不可以通过 set 的迭代器改变 set 元素的值,因为 set 元素值就是其键值,关系到 set 元素的排序规则。如果任意改变 set 元素值,会严重破坏 set 的组织。换句话说 set 的 iterator
是一种 const iterator
。set 拥有和 list 某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。
C++ 进阶基础之八
大纲
- C++ 进阶基础之一、C++ 进阶基础之二、C++ 进阶基础之三
- C++ 进阶基础之四、C++ 进阶基础之五、C++ 进阶基础之六
- C++ 进阶基础之七、C++ 进阶基础之八、C++ 进阶基础之九
- C++ 进阶基础之十、C++ 进阶基础之十一
list 容器
list 容器的概念
list 是一个双向链表容器,而且还是一个双向循环链表,可以高效地进行插入和删除元素。链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的(如下图所示)。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。相较于 vector 的连续线性空间,list 就显得负责许多,它的好处是每次插入或者删除一个元素,就是配置或者释放一个元素的空间。因此,list 对于空间的运用有绝对的精准,一点也不浪费。值得一提的是,对于任何位置的元素插入或元素的移除,list 永远是常数时间的耗时(效率较高);但对于查询操作来说,list 的执行效率较低。
总结
- 链表采用动态内存分配,不会造成内存浪费和溢出。
- 链表虽然灵活,但是空间和时间的额外耗费较大。
- 链表执行插入和删除操作都十分方便,仅修改指针即可实现,不需要移动大量元素。
- 链表不可以随机存取元素,所以不支持
at.(pos)
函数与[]
操作符的使用。
list 容器的迭代器
list 容器不能像 vector 一样以普通指针作为迭代器,因为其节点不能保证都在同一块连续的内存空间上。list 迭代器必须有能力指向 list 的节点,并有能力进行正确的递增、递减、取值、成员存取操作。所谓 “list 正确的递增、递减、取值、成员取用” 是指,递增时指向下一个节点,递减时指向上一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员。由于 list 是一个双向链表,迭代器必须能够具备前移、后移的能力,所以 list 容器提供的迭代器是 BidirectionalIterators
。list 有一个重要的性质,插入操作和删除操作都不会造成原有 list 送代器的失效。这在 vector 是不成立的,因为 vector 的插入操作可能造成记忆体重新配置,导致原有的送代器全部失效;甚至 list 元素的删除,也只有被删除的那个元素的迭代器失效,其他迭代器不受任何影响。
list 容器的使用
list 的构造与赋值
默认构造函数
list 采用模板类实现,对象的默认构造形式:list<T> lstT;
,其中 <>
尖括号内还可以设置指针类型或自定义类型
1 | list<int> lstInt; // 定义一个存放 int 数据的 list 容器 |
有参构造函数
1 | list(beg, end); // 构造函数将 [beg, end) 区间中的元素拷贝给本身,注意该区间是左闭右开的区间 |
赋值操作说明
1 | list.assign(beg, end); // 构造函数将 [beg, end) 区间中的元素拷贝给本身,注意该区间是左闭右开的区间 |
构造与赋值的使用
1 | #include <iostream> |
程序运行输出的结果如下:
1 | ------ list 构造函数 ------ |
list 的常用操作
1 | #include <iostream> |
程序运行输出的结果如下:
1 | ------ list 插入操作 ------ |
list 的反转与排序操作
提示
- 所有不支持随机访问的容器,都不可以使用系统提供的排序算法。
- 如果容器不支持使用系统提供的排序算法,那么这个容器的内部往往会提供对应的排序算法。
1 | #include <iostream> |
程序运行输出的结果如下:
1 | ------ list 反转操作 ------ |
list 的自定义数据类型操作
提示
- 对 list 的自定义类型数据类型进行排序时,必须指定排序规则。
- 调用
remove()
函数移除 list 容器中的元素时,自定义数据类型必须重载==
双等号操作符,否则移除操作会执行失败。
1 | #include <iostream> |
程序运行输出的结果如下:
1 | ------ list 插入操作(自定义数据类型) ------ |
stack 容器
stack 容器的概念
stack 是堆栈容器,属于一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。stack 容器允许新增元素、移除元素、取得栈顶元素,但是除了最顶端的元素外,没有任何其他方法可以存取 stack 中的其他元素。stack 没有迭代器,容器中所有元素的进出都必须符合 “先进后出” 的规则,只有 stack 最顶端的元素,才有机会被外界取用。换言之,stack 不提供遍历功能,也不提供迭代器。deque 是双向开口的数据结构,若以 deque 为底部结构并封闭其头端开口,便轻而易举地形成一个 stack。因此,SGI STL 便以 deque 作为缺省情况下的 stack 底部结构。由于 stack 以 deque 做为底部容器完成其所有工作,而具有这种 “修改某物接口,形成另一种风貌” 的性质者,称为 adapter(配接器)
,因此,stack 往往不被归类为 container(容器)
,而被归类为 container adapter(容器配接器)
。简而言之,stack 是简单地装饰 deque 容器而成为另外的一种容器。
C++ 进阶基础之七
大纲
- C++ 进阶基础之一、C++ 进阶基础之二、C++ 进阶基础之三
- C++ 进阶基础之四、C++ 进阶基础之五、C++ 进阶基础之六
- C++ 进阶基础之七、C++ 进阶基础之八、C++ 进阶基础之九
- C++ 进阶基础之十、C++ 进阶基础之十一
vector 容器
vector 容器的概念
vector 的数据存储以及操作方式,与 Array 非常相似,两者的唯一差别在于空间运用的灵活性。Array 是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎的细节得由自己来实现;首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector 是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此 vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就初始化一个大的 Array 了。Vector 的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦 vector 旧空间满了,如果客户每新增一个元素 vector 内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚才所说,是 “配置新空间 - 数据移动 - 释放旧空间” 的大工程,时间成本很高,应该加入某种未雨绸缪的考虑。
JavaScript 常用代码块
一、日期处理
1. 时间格式化
该方法可以用于将时间转化为 hour:minutes:seconds
的格式:
1 | const timeFromDate = date => date.toTimeString().slice(0, 8); |
2. 检察日期是否有效
该方法用于检测给出的日期是否有效:
1 | const isDateValid = (...val) => !Number.isNaN(new Date(...val).valueOf()); |