当前位置: 首页 > news >正文

【c++】vector实现(源码剖析+手画图解)

        vector是我接触的第一个容器,好好对待,好好珍惜!

目录

文章目录

前言

二、vector如何实现

二、vector的迭代器(原生指针)

三、vector的数据结构

图解:

四、vector的构造及内存管理

1.push_back()

代码如下:

2.pop_back()

 代码如下:

3.insert()

图解:

代码如下:

需要注意的是insert()迭代器失效问题: 

4.erase()

图解:

代码如下:

需要注意的是erase()的迭代器失效问题:

给出三种验证的例子:

图解:

五、vector的reserve与resize

1.reserve()

代码如下:

需要注意的是memcpy()的浅拷贝问题:

改进版:

代码如下:

2.resize()

代码如下:

六、vector类的基本成员函数

1.default构造

代码如下:

2.copy构造

1)传统写法

代码如下:

需要注意的仍然是memcpy()的浅拷贝:

改进版:

2)现代写法

代码如下:

3.用迭代器区间的copy构造函数

4.析构

代码如下:

七、其他的一些函数

1.获取size

代码如下:

2.获取capacity

代码如下:

3.访问元素

代码如下:

4.迭代器 -> 使用范围for

代码如下:

5.赋值重载

1)传统写法

需要注意的仍然是memcpy()的浅拷贝:

改进版:

2)现代写法

代码如下:

6.swap()

代码如下:

总结



前言

        vector实现时最难想的是赋值重载时的现代写法,很妙,最有趣的是两种迭代器的失效问题。与数组相同,但又多少有点不同。


一、vector是什么?

        vector是一个序列式容器(其中的元素都可序(ordered),但是未必有序(sorted)),本质上是可变数组,尾插尾删效率较高。

二、vector如何实现

        源代码中vector的实现使用到了alloc(空间配置器),但是鉴于自身技术水平,个人只在本文中阐述如何实现无alloc版本的vector。

二、vector的迭代器(原生指针)

        因为vector是一段连续的物理存储空间,实现增删查改时,需要对位置进行++、--、+、-、+=、-=、->,这些都是普通指针就具备的能力,所以vector提供的是Random Access Iterators。

代码如下(示例):

template<typename T>class vector{public:		typedef T valueType;typedef T* iterator;private:iterator _start;iterator _finish;iterator _endOfStorage;}

三、vector的数据结构

        vector是一段连续的物理空间,其中SGI版本使用了三个指针去管理这个变长数组、使用了两个unsigned int变量去标识有效字符与容量

图解:


四、vector的构造及内存管理

        与之前学习数据结构实现的动态顺序表内存管理其实大同小异:一开始有一块默认大小的内存;

增:

        ①内存不够再存新元素:扩容,一般扩至原capacity的2倍

        ②内存够:直接存。

删:

        ①减少元素个数但是不缩容(大部分版本)。

1.push_back()

代码如下:

void push_back(const valueType& x) {// 增容if (_finish == _endOfStorage){reserve((capacity() == 0) ? 4 : capacity() * 2);}// 尾插*_finish = x;++_finish;}

2.pop_back()

 代码如下:

// 尾删void pop_back(){assert(_finish > _start);--_finish;}

3.insert()

        原stl给出的模板中,insert()是返回了一个迭代器,指向插入元素的位置。        

        目的:若迭代器已经失效,尽管在增容操作中更新迭代器的值进行规避风险,但是调用时的迭代器仍为野指针(失效状态),若后续操作需要使用插入元素位置,那就会在此出现问题,所以可以再用这个迭代器接受insert()的返回值来进行更新。

图解:

代码如下:

// 随机插入iterator insert(iterator pos, const valueType& val){// 最小在头插,最大在尾插assert(pos >= _start);assert(pos < _finish);//增容,会使迭代器失效if (_finish == _endOfStorage){// 更新迭代器的值size_t len = pos - _start;reserve((capacity() == 0) ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;// 元素个数+1,_finish+1++_finish;return pos;}

需要注意的是insert()迭代器失效问题: 

        当插入时,vector进行了扩容操作,这一步会:

        ①使用一块新的、够大的tmp空间去拷贝原来_start指向的数据

        ②将原来的_start指向的空间释放

        ③把tmp的空间给_start

        但是此时,传进来的、想要插入数据的迭代器仍然指向原来的_start空间,这个迭代器就变成了野指针,再下一步对这个迭代器指向位置进行操作时就会非法访问,所以需要在进行扩容操作是对于迭代器也进行相应的更新,尽量避免失效问题。 

4.erase()

        原stl给出的模板中,erase()是返回了一个迭代器,指向删除元素的位置的下一个位置。

        目的:同insert()相同。

        Q:为什么指向的是操作位置的下一个位置?

        A:因为删除操作本质上是对数据的覆盖,这样一来,从操作位置开始向后的数据向前挪动也相当于操作位置向后移动了一位(指向了下一个位置)。 

图解:

代码如下:

// 随机删除iterator erase(iterator pos){// 最小头删,最大尾删assert(pos >= _start);assert(pos < _finish);auto begin = pos + 1;while (begin != _finish){*begin = *(begin + 1);++begin;}// 删除一个元素,_finish向前移动--_finish;// 返回的是删除元素的下一个元素return pos;}

需要注意的是erase()的迭代器失效问题:

        erase()的迭代器失效主要是因为迭代器的意义改变,当然,也有野指针的问题。

        ps:但是这个无法通过我们改变erase()实现去规避。

        ①意义改变:当数据向前挪动时,这个迭代器已经不再是我们想要的迭代器了,他一次性向后跳了两个位置。

        ②野指针:当数据向前挪动时,迭代器可能跳过了_finish(当第一次与_finish相遇时,begin++的操作还在继续进行),从而继续向后移动,宛如脱缰野马,永远不可能再停下(与_finish相遇才会结束)。

给出三种验证的例子:

        均是删除给定数据中的偶数元素:        

        1 2 3 4 5 - > 迭代器意义改变,但是刚好这个bug没有使结果出现问题

        1 2 3 4 - > 迭代器

        1 2 4 5 - > 4被跳过(迭代器每次相当于向后移动2位),未删除

图解:


五、vector的reserve与resize

1.reserve()

        改变的是容器的容量,需要进行开辟空间、释放空间、拷贝数据的操作,效率不是很高,也正因为扩容的效率不高,所以才有了_endofStorage和_finish去标识容量与元素个数。

代码如下:

void reserve(size_t newCapacity){// 扩容valueType* tmp = new valueType[newCapacity]();size_t sz = size();// 如果有数据,需拷贝到新空间if (_start){memcpy(tmp, _start, sizeof(valueType) * size());}// 释放旧空间delete[] _start;_start = tmp;_finish = _start + sz;_endOfStorage = _start + newCapacity;}

需要注意的是memcpy()的浅拷贝问题:

        因为memcpy()进行的是位操作,如果copy的是内置类型(int、char等)那么直接使用memcpy()会效率很高很方便;但是牵扯到指针,这会导致内存错误:

        当两个指针的内容相同,那就意味着它们指向了同一块空间,如果一个指针释放,另一个指针仍对其进行操作就会使得内存发生错误。

改进版:

        使用for循环自己一个元素一个元素进行拷贝。

代码如下:

void reserve(size_t newCapacity){// 扩容valueType* tmp = new valueType[newCapacity]();size_t sz = size();// 如果有数据,需拷贝到新空间if (_start){//memcpy(tmp, _start, sizeof(valueType) * size());for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}}// 释放旧空间delete[] _start;_start = tmp;_finish = _start + sz;_endOfStorage = _start + newCapacity;}

2.resize()

        改变的是元素的个数(size)。

        ①如果newSize比size小,那么就发生了删除,直接改变_finish即可,但是不缩容(以防后续操作频繁删除增容、频繁向操作系统申请、释放内存造成抖动)。

        ②如果newSize比size大,那么就可能发生了扩容(需检查)。

                1.若给出初始化的值,那么根据给出的值进行初始化

                2.若无给出的值,默认是空,在模板中是const vaueType& val = valueType(),构造了空的对象进行缺省参数的赋值。

代码如下:

void resize(size_t newSize, const valueType& x = valueType()){// newSize比size短,截断// 但是容量不变if (_finish + newSize < _endOfStorage){_finish = _start + newSize;}// 长,增长并初始化else{reserve(newSize);while (_finish != _start + newSize){*_finish = x;_finish++;}}}

六、vector类的基本成员函数

1.default构造

        三个原生指针,初始化为空指针即可。

代码如下:

// 构造vector():_start(nullptr), _finish(nullptr), _endOfStorage(nullptr){}

2.copy构造

1)传统写法

        传统写法就是自己一步一步实现深拷贝的过程。

代码如下:

// copy构造// 传统写法vector(const vector<valueType>& obj){_start = new valueType[obj.size()];_finish = _start + obj.size();_endOfStorage = _start + obj.capacity();memcpy(_start, obj._start, obj.size() * sizeof(valueType));}

需要注意的仍然是memcpy()的浅拷贝:

        我们应该使用for循环去自己实现深拷贝避免内存问题。

改进版:

// 传统写法vector(const vector<valueType>& obj){_start = new valueType[obj.size()];_finish = _start + obj.size();_endOfStorage = _start + obj.capacity();//memcpy(_start, obj._start, obj.size() * sizeof(valueType));for (size_t i = 0; i < size(); i++){_start[i] = obj._start[i];}}

2)现代写法

        使用tmp变量,让其使用构造函数构造出我们想要copy的对象,然后将tmp和我们将要copy的对象进行交换,从而帮助我们实现copy构造。

        swap(tmp, *this)这一点就是将*this中的垃圾扔给了tmp,tmp还把自己有用的数据全部交给了*this,就好比是外卖员替你送了外卖还得帮你扔垃圾。

        但是很明显的是:已有的构造函数并不能根据传进来的obj对tmp进行构造。我们需要重载实现一个支持用迭代器区间的copy构造函数(stl本来也支持这样的方式构造)。(见下一点)

        ps:copy构造也是构造,别忘了初始化!

代码如下:

// 现代写法//类模板的成员函数也可以变成函数模板vector(const vector<valueType>& obj):_start(nullptr),_finish(nullptr),_endOfStorage(nullptr){vector<valueType> tmp(obj.begin(), obj.end());// “外卖员送饭又倒垃圾的例子”swap(tmp);}

3.用迭代器区间的copy构造函数

        我们实现为了模板函数,这一点是在套娃:

        本来就是类模板的成员函数,其可以用函数模板去实现。

        目的:

        其他的迭代器(string、int、char等)都可以通过迭代器去构造。

// 迭代器版的copy构造template <class InputIterator>vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endOfStorage(nullptr){while (first != last){this->push_back(*first);first++;}}

4.析构

        析构是释放空间,因为三个指针指向的是同一段空间,所以只需要释放_start。

        然后将它们置为空。

        ps:new时候使用的[],所以delete也需要[]进行释放。

代码如下:

// 析构~vector(){delete[] _start;_start = _finish = _endOfStorage = nullptr;}

七、其他的一些函数

1.获取size

        回想我们之前的结构:

        size就是_finish与_start之间的距离,使用两个指针相减即可得到。

代码如下:

size_t size() const{return _finish - _start;}

2.获取capacity

        同size()。

代码如下:

size_t capacity() const{return _endOfStorage - _start;}

ps:但是需要注意的是这两个成员函数最好实现为const成员函数,因为在copy构造与的default构造时传的参数均使用了const。(常对象与非常对象都可以用const)

3.访问元素

        数组可以通过下标访问。

代码如下:

const valueType& operator[](size_t i){return _start[i];}

4.迭代器 -> 使用范围for

        实现为const,原理同size()、capacity()。

代码如下:

// 迭代器// 必须是begin与end(同名)才能使用范围foriterator begin() const{return _start;}iterator end() const{return _finish;}

5.赋值重载

        赋值重载与copy构造总是很相像:

        ①赋值重载是相对于两个已经存在的对象之间的操作。

        ②copy构造是相对于一个即将创建一个已经存在的对象之间的操作。

        

        但是它们最终要达到的目标都是相同的:让两个对象相等。

        

        所以在实现过程上也大同小异。

        实现赋值重载需要注意的点是:

        ①判断是否给自己赋值

        ②需要传引用返回,减少拷贝

        ③传进来的函数参数需要是const的引用,防止修改+减少拷贝。

        ④返回值是*this

1)传统写法

                与copy构造相同的是,它也要实现:释放旧空间、开辟新空间、拷贝数据。

// 传统写法vector<valueType>& operator=(const vector<valueType>& obj){// 判断是否给自己赋值if (this == &obj){return *this;}delete[] _start;//_start = (valueType*)calloc(obj.capacity(), sizeof(valueType));_start = new valueType[obj.capacity()];_finish = _start + obj.size();_endOfStorage = _start + obj.capacity();memcpy(_start, obj._start, sizeof(obj) * obj.size());return *this;}

需要注意的仍然是memcpy()的浅拷贝:

        我们使用for循环自己进行深拷贝。

改进版:

// 传统写法vector<valueType>& operator=(const vector<valueType>& obj){// 判断是否给自己赋值if (this == &obj){return *this;}delete[] _start;//_start = (valueType*)calloc(obj.capacity(), sizeof(valueType));_start = new valueType[obj.capacity()];_finish = _start + obj.size();_endOfStorage = _start + obj.capacity();//memcpy(_start, obj._start, sizeof(obj) * obj.size());for (size_t i = 0; i < obj.size(); i++){_start[i] = obj._start[i];}return *this;}

2)现代写法

        这种写法很巧妙地使用了传值传参需要进行拷贝构造的特点,利用这一点在传参的时候就可以深拷贝出来一个obj,这样进行交换的时候就不会把原来的obj更改。

代码如下:

/ 赋值重载 v2 = v1// 现代写法vector<valueType>& operator=(vector<valueType> obj){swap(obj);return *this;}

6.swap()

        由于copy构造与赋值重载都使用了交换,所以将它实现为了函数。

代码如下:

void swap(vector<valueType>& tmp) {std::swap(tmp._start, _start);std::swap(tmp._finish, _finish);std::swap(tmp._endOfStorage, _endOfStorage);}

总结

        vector的实现是对于stl源码的一次深入理解,但是仍然没有学会alloc,只学习了一部分,自己模拟实现vector,不在于造一个更好的轮子,但是可以帮助我更好的了解它、使用它。

相关文章:

【c++】vector实现(源码剖析+手画图解)

vector是我接触的第一个容器&#xff0c;好好对待&#xff0c;好好珍惜&#xff01; 目录 文章目录 前言 二、vector如何实现 二、vector的迭代器&#xff08;原生指针&#xff09; 三、vector的数据结构 图解&#xff1a; 四、vector的构造及内存管理 1.push_back() …...

VScode查看python f.write()的文件乱码

VScode查看python f.write()的文件乱码 在使用 VScode 编写 python 代码&#xff0c; print&#xff08;&#xff09;&#xff0c;汉字正常显示&#xff0c; 使用 with open&#xff08;&#xff09;as f&#xff1a; f.write&#xff08;&#xff09;文件后&#xff0c; 在 …...

excel应用技巧:如何用函数制作简易抽奖动图

利用INDEX函数和随机整数函数RANDBETWEEN配合&#xff0c;在Excel中做一个简单的抽奖器&#xff0c;可以随机抽取姓名或者奖品。有兴趣的伙伴可以做出来试试&#xff0c;撞撞2023年好运气。每次年会大家最期待的就是抽奖环节。为了看看自己今年运气怎么样&#xff0c;会不会获奖…...

CSI Tool 安装及配置记录

一、Ubuntu安装 1.下载Ubuntu 首先安装Ubuntu 14.04 LTS 64位下载地址&#xff08;页面中第一个链接&#xff09; 2.制作启动盘&#xff08;注意备份&#xff09; 可以使用官方的工具Rufus&#xff0c;下载地址&#xff1a;https://rufus.ie/ 打开Rufus&#xff0c;先备份…...

华为OD机试 - 最低位排序(Python)| 真题+思路+代码

最低位排序 题目 给定一个非空数组(列表),起元素数据类型为整型, 请按照数组元素十进制最低位从小到大进行排序, 十进制最低位相同的元素,相对位置保持不变, 当数组元素为负值时,十进制最低为等同于去除符号位后对应十进制值最低位。 输入 给定一个非空数组(列表) 其…...

C#开发的OpenRA使用TrimExcess方法

C#开发的OpenRA使用TrimExcess方法 当你在细看OpenRA的代码,就会发现在下面这段代码添加了一个方法: foreach (var nodes in levels) nodes.TrimExcess(); 在上面代码里遍历整个节点列表,把所有节点都调用TrimExcess方法处理一下, 这样做的意义何在?为什么我们在一般的代码…...

ImageMagick任意文件读取漏洞(CVE-2022-44268)

0x00 前提 前几天爆出一个 ImageMagick 漏洞 &#xff0c;可以造成一个任意文件读取的危害比较可观&#xff0c;最近有时间来复现学习一下 主要是影响的范围很大&#xff0c;很多地方都有这个问题&#xff0c;需要来学习一下 0x01 介绍 ImageMagick 是一个免费的开源软件套…...

第十九篇 ResNet——论文翻译

文章目录 摘要1 引言2 相关工作3 深度残差学习3.1 残差学习3.2 快捷恒等映射3.3 网络架构3.4 实现4 实验4.1 ImageNet 分类4.2 CIFAR-10 和分析4.3 PASCAL 和 MS COCO 上的物体检测🐇🐇🐇🐇🐇🐇 🐇 欢迎阅读 【AI浩】 的博客🐇 👍 阅读完毕,可以动动小手赞一…...

RiProRiProV2主题美化顶部增加一行导航header导航通知

背景: 有些网站的背景顶部有一行罪行公告,样式不错,希望自己的网站也借鉴过来,本教程将指导如何操作,并调整成自己想要的样式。 比如网友搭的666资源站 xd素材中文网...

RT-Thread MSH_CMD_EXPORT分析

RT-Thread MSH_CMD_EXPORT分析 1. 源码分析 在rt-thread中&#xff0c;使用FinSH&#xff0c;可以支持命令行。在源码中&#xff0c;使用MSH_CMD_EXPORT导出函数到对应命令。 extern void rt_show_version(void); long version(void) {rt_show_version();return 0; } MSH_CM…...

电脑麦克风没声音怎么办?这3招就可以解决!

最近有用户在使用电脑麦克风进行视频录制时&#xff0c;发现麦克风没有声音。这是什么原因&#xff1f;电脑麦克风没有声音怎么办&#xff1f;关于解决方案&#xff0c;我专门整理了三种方法来帮你们&#xff0c;一起来看看吧&#xff01; 操作环境&#xff1a; 演示机型&#…...

【C++】运算符重载

运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也具有其返回值类型&#xff0c;函数名以及参数列表。其返回值类型和参数列表与普通的函数类型。 函数名字为&#xff1a;关键字operator后面接需要重载的运算符号…...

什么是眼图?(扫盲向)

什么是眼图&#xff1f;&#xff08;扫盲向&#xff09; Ref: What’s eye diagram? 1 基础图示 眼图 2 用途 常用于评估差分链路中的信号传输质量 "眼睛"张得越开&#xff0c;链路信号质量越好 3 观测原理 眼图是传输信号序列在时域上的叠加 4 观测参数 4…...

【C++】类与对象(二)

前言 在前一章时我们已经介绍了类与对象的基本知识&#xff0c;包括类的概念与定义&#xff0c;以及类的访问限定符&#xff0c;类的实例化&#xff0c;类的大小的计算&#xff0c;以及C语言必须传递的this指针&#xff08;C中不需要我们传递&#xff0c;编译器自动帮我们实现&…...

【软考】系统集成项目管理工程师(二十一)项目收尾管理

1. 项目验收2. 项目总结3. 系统维护4. 项目后评价补充:人员转移和资源遣散广义的系统集成项目收尾管理工作通常包含四类典型的工作:项目验收工作、项目总结工作、系统维护工作 以及 项目后评价工作,此外项目团队成员的后续工作也应在收尾管理时妥善安排;狭义的系统集成项目…...

关于公钥与私钥的一点看法

故事的起源 私密性 之前&#xff0c;用户a想给用户b发消息&#xff0c;a希望他自己发出现的消息&#xff0c;只能被b读懂。也就是说a希望发出去的数据是被加密过的&#xff0c;收到消息的人可以是b&#xff0c;c&#xff0c;d&#xff0c;e等等。但是只有b能被读懂。 这个需求…...

深入React源码揭开渲染更新流程的面纱

转前端一年半了&#xff0c;平时接触最多的框架就是React。在熟悉了其用法之后&#xff0c;避免不了想深入了解其实现原理&#xff0c;网上相关源码分析的文章挺多的&#xff0c;但是总感觉不如自己阅读理解来得深刻。于是话了几个周末去了解了一下常用的流程。也是通过这篇文章…...

32个关于FPGA的学习网站

语言类学习网站 1、HDLbits 网站地址&#xff1a;https://hdlbits.01xz.net/wiki/Main_Page 在线作答、编译的学习Verilog的网站&#xff0c;题目很多&#xff0c;内容丰富。非常适合初学Verilog的人&#xff01;&#xff01;&#xff01; 2、牛客网 网站地址&#xff1a;http…...

5分钟快速上手Promise使用

promise 是处理异步编程的一种处理方式&#xff0c;可以将异步操作按照同步操作的方式编写。是一个对象或者构造函数&#xff0c;里面存放着某个未来才会执行的结果的方法&#xff08;一般就是异步操作&#xff09; 自己身上有all、reject、resolve这几个方法&#xff0c;原型上…...

大客户市场:阿里云、腾讯云、华为云“贴身肉搏”

配图来自Canva可画 近年来&#xff0c;随着中国逐渐进入数字化经济快车道&#xff0c;国内企业数字化、智能化升级已是刻不容缓。而为了帮助自身或其他企业实现数字化转型升级&#xff0c;阿里、腾讯、百度、京东、字节、网易、华为等众多国内知名企业早在多年以前&#xff0c…...

华为OD机试 - 求字符串中所有整数的最小和(Python)| 真题+思路+代码

求字符串中所有整数的最小和 题目 说明 字符串 s,只包含 a-z A-Z + - ;合法的整数包括 1) 正整数 一个或者多个0-9组成,如 0 2 3 002 102 2)负整数 负号 - 开头,数字部分由一个或者多个0-9组成,如 -0 -012 -23 -00023输入 包含数字的字符串 输出 所有整数的最小和 …...

企业电子招投标采购系统源码之首页设计

​​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为…...

浅谈一下前端工作中全流程多层次的四款测试工具

在应届生找工作的时候&#xff0c;我们经常会见到一条招聘要求&#xff1a;要求实习经历。或者 有实习经历者优先。 为什么大部分公司在招聘时&#xff0c;都要求你必须有实习经历&#xff1f; 商业项目与个人项目不同&#xff0c;一段实习经历&#xff0c;能够熟悉公司中成熟…...

【运算放大器】反相放大电路仿真应用

目录 一、反相放大电路原理&#xff08;简化电路&#xff09; 二、反相放大电路电路原理&#xff08;实际特性&#xff09; 2.1原理图 2.2实际电路 三、虚短 虚断 3.1 虚短 3.2 虚断 四、作业 4.1 &#xff08;反相&#xff09;放大电路设计 4.2 LM741芯片 五、标准…...

数组的操作

1.splice 1.splice 是数组的一个方法&#xff0c;使用这个方法会改变原来的数组结构&#xff0c;splice&#xff08;index &#xff0c;howmany &#xff0c; itemX&#xff09;&#xff1b;这个方法接受三个参数&#xff0c;我们在使用的时候可根据自己的情况传递一个参数&…...

Python - 文件基础操作

目录 文件的读取 open()打开函数 read类型 read()方法 readlines()方法 readline()方法 for循环读取文件行 close() 关闭文件对象 with open 语法 文件的写入 文件的追加 文件的读取 操作 功能 文件对象 open(file, mode, encoding) 打开文件获得文件对象 文件…...

react的useState源码分析

前言 简单说下为什么React选择函数式组件&#xff0c;主要是class组件比较冗余、生命周期函数写法不友好&#xff0c;骚写法多&#xff0c;functional组件更符合React编程思想等等等。更具体的可以拜读dan大神的blog。其中Function components capture the rendered values这句…...

SharpImpersonation:一款基于令牌和Shellcode注入的用户模拟工具

关于SharpImpersonation SharpImpersonation是一款功能强大的用户模拟工具&#xff0c;该工具基于令牌机制和Shellcode注入技术实现其功能&#xff0c;可以帮助广大研究人员更好地对组织内部的网络环境和系统安全进行分析和测试。 该工具基于 Tokenvator的代码库实现其功能&a…...

华为OD机试 - 最大相连男生数(Python)| 真题+思路+代码

最大相连男生数 题目 学校组织活动,将学生排成一个矩形方阵。 请在矩形方阵中找到最大的位置相连的男生数量。 这个相连位置在一个直线上,方向可以是水平的、垂直的、成对角线的或者反对角线的。 注:学生个数不会超过 10000。 输入 输入的第一行为矩阵的行数和列数,接下…...

GIS在地质灾害危险性评估与灾后重建中的实践技术应用及python机器学习灾害易发性评价模型建立与优化

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…...

龙华品牌网站制作/谷歌推广一年多少钱

点分治 点分治可以用来处理有关树上路径的问题 首先选取当前子树的重心作为分治点&#xff0c;因为重心可保证最大的子树不超过&#xff08;u/2&#xff09;&#xff0c;这样每次递归的处理下去&#xff0c;复杂度是&#xff08;nlogn&#xff09;的 求重心代码&#xff1a; vo…...

wordpress自定义文章目录/品牌运营推广方案

本文描述了在dotNet核心中使用像以太坊这样的区块链平台的过程。目标受众是其他想要从以太坊开始的dotNet开发者。需要了解区块链。在本文中&#xff0c;我们构建了一个完整的示例&#xff0c;允许你与自定义编写的智能合约进行交互。 第一代区块链的可以被视为仅比特币而没有…...

网站模板站的模板展示怎么做的/网站优化与seo

方法一&#xff1a;伪列布局法(利用背景图)所谓伪列布局法&#xff0c;就是设计一个背景图像&#xff0c;利用背景图像来模拟栏目的背景。如&#xff0c;使用Photoshop设计一个长方形的背景图&#xff0c;长度与页面宽度保持一致&#xff0c;高度任意。代码中要用到的背景图&am…...

绵阳市公司网站建设/建站的公司

1. AOP 初窥 比如如下一段代码&#xff0c;仅仅根据商品 Id 获取商品信息 public GoodsVo getGoodsVoByGoodsId(long goodsId) {return goodsMapper.getGoodsVoByGoodsId(goodsId); }有时候为了便于观察执行情况&#xff0c;会在前后加上日志&#xff0c;这种都很常见吧。 p…...

医疗行业企业网站建设/私域流量运营管理

内置函数: 函数分类: 内置函数查看: show funcitons; 查看函数描述: DESC FUNCTION concat; 具体见: https://cwiki.apache.org/confluence/display/Hive/LanguageManualUDF 1, 简单函数( 函数的计算粒度为单条记录) 关系运算 数学运算 逻辑运算 数值计算 类型转换 日期函数…...

网站空间怎么收费/今日头条热搜榜

nautilus 图形化桌面包括了一个叫做 Nautilus 的文件管理器 在GNOME中是Nautilus(鹦鹉螺)&#xff0c;而KDE中是Konqueror...