【C++】vector的使用及模拟实现
目录
- 一、vector的介绍及使用
- 1.1 介绍vector
- 1.2 vector的使用
- 1.2.1 构造
- 1.2.2 遍历访问
- 1.2.3 容量空间
- 1.2.4 增删查改
- 二、vector的模拟实现
- 2.1 成员变量
- 2.2 迭代器相关函数
- 2.3 构造-析构-赋值重载
- 2.3.1 无参构造
- 2.3.2 有参构造1
- 2.3.3 有参构造2
- 2.3.4 拷贝构造
- 2.3.5 赋值重载
- 2.3.6 析构
- 2.4 容量操作
- 2.4.1 size和capacity
- 2.4.2 reserve
- 2.4.3 resize
- 2.5 插入与删除
- 2.5.1 尾插
- 2.5.2 尾删
- 2.5.3 pos位置插入
- 2.5.3 pos位置删除
- 2.6 遍历访问
- 2.7 全部代码
- 2.7.1 vector.h
- 2.7.2 test.cpp
一、vector的介绍及使用
1.1 介绍vector
vector是一个可变大小数组的容器,与数组一样,vector也是一块连续的空间,可以像数组一样对元素进行高效的遍历访问,但是普通数组的大小是不变的,vector可以改变自身大小。vector是采用动态分配数组来存储数据,即插入新元素时要改变存储空间大小,往往要分配一个新的数组,然后把原来数组的元素转移到新的空间里。vector的尾插尾删效率高,中间插入和删除效率较低。
1.2 vector的使用
1.2.1 构造
1️⃣无参
vector()
vector<int> v;
2️⃣构造并初始化n个val
vector(size_type n, const value_type& val = value_type())
vector<int> v(10, 7);
3️⃣拷贝构造
vector (const vector& x)
vector<int> v1{ 1,2,3,4 };vector<int> v2(v1);
4️⃣使用迭代器进行初始化构造
vector (InputIterator first, InputIterator last)
vector<int> v1{ 5,6,7,8};vector<int> v2(v1.begin(), v1.end());
1.2.2 遍历访问
1️⃣begin+end
vector<int> v{ 1,2,3,4,5 };auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
获取数组第一个元素的位置,像指针一样遍历整个数组,直到最后一个元素结束。auto是自动推导类型。
2️⃣下标:operator[ ]
vector<int> v{ 1,2,3,4,5 };for (int i = 0; i < 5; i++){cout << v[i] << " ";}cout << endl;
3️⃣范围for
vector<int> v{ 1,2,3,4,5 };for (auto e : v){cout << e << " ";}cout << endl;
1.2.3 容量空间
1️⃣获取元素个数
vector<int> v{ 1,2,3,4,5 };cout << v.size() << endl;//5
2️⃣获取容量大小
vector<int> v{ 1,2,3,4,5 };cout << v.capacity() << endl;//5
3️⃣判断是否为空
vector<int> v;cout << v.empty() << endl;
4️⃣改变vector的size
void resize (size_type n, value_type val = value_type());
vector<int> v{ 1,2,3,4,5 };for (auto e : v){cout << e << " ";}cout << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.resize(10, 9);cout << endl;for (auto e : v){cout << e << " ";}cout << endl;cout << v.size() << endl;cout << v.capacity() << endl;
如果超出原来的存储空间,那么capacity也会改变
5️⃣改变vector的capacity
void reserve (size_type n);
vector<int> v{ 1,2,3,4,5 };for (auto e : v){cout << e << " ";}cout << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.reserve(10);cout << endl;for (auto e : v){cout << e << " ";}cout << endl;cout << v.size() << endl;cout << v.capacity() << endl;
1.2.4 增删查改
1️⃣尾插
vector<int> v{ 1,2,3,4,5 };v.push_back(9);for (auto e : v){cout << e << " ";}cout << endl;
2️⃣尾删
vector<int> v{ 1,2,3,4,5 };v.pop_back();for (auto e : v){cout << e << " ";}cout << endl;
3️⃣查找
注意查找不是vector的接口,是算法模块实现的。
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
vector<int> v{ 1,2,3,4,5 };auto it = find(v.begin(), v.end(), 2);cout << *it << endl;//2
4️⃣交换
void swap (vector& x);
vector<int> v1{ 1,2,3,4,5 };vector<int> v2{ 6,7,8,9,10 };v2.swap(v1);
5️⃣在pos位置插入
iterator insert (iterator position, const value_type& val);
vector<int> v{ 1,2,3,4,5 };auto pos = find(v.begin(), v.end(), 2);v.insert(pos, 10);for (auto e : v){cout << e << " ";}cout << endl;
6️⃣在pos位置删除
iterator erase (iterator position);//指定位置删除
iterator erase (iterator first, iterator last);//指定范围删除
有两种写法:
// 一:vector<int> v{ 1,2,3,4,5 };auto pos = find(v.begin(), v.end(), 2);v.erase(pos);for (auto e : v){cout << e << " ";}cout << endl;
// 二:vector<int> v{ 1,2,3,4,5 };v.erase(v.begin() + 1, v.end() - 1);for (auto e : v){cout << e << " ";}cout << endl;
注意:insert 和 erase 一般只使用一次,重复使用可能导致迭代器失效。
二、vector的模拟实现
2.1 成员变量
vector的迭代器是一个原生指针,它的三个成员变量分别是:
typedef T* iterator;typedef const T* const_iterator;//————————iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;
这里在声明时先给成员变量缺省值为空指针,声明时给缺省值是给初始化列表的,后续写的时候就比较方便。
2.2 迭代器相关函数
begin函数返回空间的起始位置,end函数返回空间的最后一个有效元素的下一位。
// 通过迭代器访问元素时可修改iterator begin(){return _start;}iterator end(){return _finish;}// 通过迭代器访问元素时不可修改const_iterator begin() const {return _start;}const_iterator end() const{return _finish;}
2.3 构造-析构-赋值重载
2.3.1 无参构造
没有传参数
vector(){}
2.3.2 有参构造1
代码:
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{reserve(last - first);//复用扩容函数while (first != last)//判断条件{push_back(*first);//复用尾插函数++first;//尾插一次后加1}
}
这个函数看起来有些奇怪,为啥还要套一层模板呢?使用这里就不说了,其实这个构造函数的作用是:只要传进来的是迭代器就可以初始化该迭代器的内容,前提是该迭代器是指向连续物理空间的指针。比如用vector定义了一个v1,v1已经是构造好的,有具体的元素;再定义一个v2,我想让v2的内容跟v1是一样的,就可以使用这个函数,传的迭代器指向的是v1的头和尾,它就会构造出和v1一样的内容。前面的使用方法有代码。不仅可以vector传vector,还可以list传vector,只要是迭代器即可。
2.3.3 有参构造2
vector(size_t n, const T& x = T())
{resize(n, x);//复用修改元素个数函数
}
vector(int n, const T& x = T())
{resize(n, x);//复用修改元素个数函数
}
该构造函数是初始化为n大小的空间,每个元素是x。可以直接复用resize函数,下面会介绍。这里写了两个构造函数唯一的区别是size_t 和int ,因为如果只有size_t 类型的那个构造函数,构造时不会调用该函数,会调用前面的构造函数(有参构造1),这与函数模板的匹配调用原则有关。假如传进来的n不是size_t 类型,那么它就要发生隐式类型转换,但是编译器想,隐式类型转换感觉麻烦,就使用有模板的那个函数。
为了防止出现以上情况,同时是模拟实现,要尽可能像标准库里面的vector,所以多重载了一个函数,它的参数n是int 类型的,这样的话传进来的参数n是int 类型,那么它就会直接调用这个构造函数,避免了以上情况。
2.3.4 拷贝构造
拷贝构造要注意深浅拷贝问题
1️⃣写法1
vector(const vector<T>& v)
{_start = new T[v.capacity()];//开一样大的空间memcpy(_start, v._start, sizeof(T) * v.size());//拷贝数据_finish = _start + v.size();_endofstorage = _start + v.capacity();
}
与string的拷贝构造类似,开一块新空间,拷贝数据后,指向这个新空间,这样防止两个指针指向同一块空间。
写法1还要注意memcpy的深浅拷贝问题,与元素的类型有关,这个在后面会具体介绍,现在假设统一使用的元素类型先是 int 类型。
1️⃣写法2
vector(const vector<T>& v)
{reserve(v.capacity());//复用扩容函数for (const auto& e : v)//范围for循环直接放入数据即可{push_back(e);//复用尾插函数}
}
先开与参数v一样大的空间,然后使用范围for直接放入数据,只要参数v里面有元素,都可以把元素一个一个的尾插到要构造的空间里面,同时也不影响v,最终完成拷贝构造。
2.3.5 赋值重载
1️⃣写法1
vector<T>& operator=(const vector<T>& v)
{if (this != &v)//相同就不用赋值{T* tmp = new T[v.capacity()];//临时空间memcpy(tmp, v._start, sizeof(T) * v.size());//拷贝数据delete[] _start;//清理旧空间_start = tmp;//指向新空间_finish = _start + v.size();//_endofstorage = _start + v.capacity();}return *this;
}
1️⃣写法2
与string的是一样的,直接代码:
vector<T>& operator=(vector<T> v)
{swap(v);//复用交换函数return *this;
}
//交换
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}
2.3.6 析构
如果空间里有元素才清理,让3个指针置空;没有元素本来就是空指针不能清理。
~vector()
{if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;//}
}
2.4 容量操作
2.4.1 size和capacity
根据三个指针的指向,可以确定有效元素个数和容量的区间
//返回个数
size_t size() const
{return _finish - _start;
}
//返回容量
size_t capacity() const
{return _endofstorage - _start;
}
2.4.2 reserve
如果存储的元素超出原来的空间大小要扩容,扩容要开辟一块新空间,然后拷贝数据到新空间里,再让指针重新指向这块新空间。
代码:
//扩容
void reserve(size_t n)
{if (n > capacity())//满了要扩容{size_t old = size();//记录当前元素个数T* tmp = new T[n];//新空间if(_start){//memcpy(tmp, _start, sizeof(T) * old);//将数据拷贝到新空间-浅拷贝for (size_t i = 0; i < old; i++){tmp[i] = _start[i];//深拷贝}delete[] _start;//释放就空间}_start = tmp;//指向新空间_finish = _start + old;//防止迭代器失效_endofstorage = _start + n;}
}
问题1:为什么要定义变量old
开空间拷贝数据后,旧空间被释放,指向新空间,_finish等于_start 加上元素个数,注意,如果没有old前面先记录元素个数的值,加上的是调用size函数返回的元素个数,那么就会出现迭代器失效问题。因为_start已经指向新的空间,而_finish还是指向旧空间的某个位置,那个不同空间相减就出问题了。
所以这里提前用变量old记录好元素的个数,_finsih等于_start+old,就不会出现以上情况。
问题2:memcpy是浅拷贝
元素类型是int 等内置类型没关系,如果是自定义类型,比如string,这里就会出现浅拷贝的问题。
深拷贝的做法是以赋值的形式逐个把旧空间的字符串给新空间:
2.4.3 resize
该函数可通过参数n修改元素个数,超出容量也会扩容。主要分为以下3点:
1.n小于等于元素个数
2.n大于元素个数且小于等于容量
3.n大于容量
代码:
void resize(size_t n, const T& x = T())
{if (n <= size()){_finish = _start + n;//修改_finish指向即可}else//n>size(){size_t len = n - size();//n与元素个数的差值if (n > capacity())//n大于容量要扩容{reserve(n);//复用扩容函数}while (len--)//插入len个x,只要n大于size()都要插入{*_finish = x;++_finish;}}
}
2.5 插入与删除
2.5.1 尾插
void push_back(const T& x)
{if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}*_finish = x;++_finish;
}
尾插数据,_finish 指向同_endofstorage说明空间内数据已满,要扩容。然后插入在_finish指向的位置插入数据,_finish往后移。
2.5.2 尾删
void pop_back()
{assert(size() > 0);//有元素才能删--_finish;
}
2.5.3 pos位置插入
首先断言pos的位置是否合理,然后凡是插入数据,都要检查是否需要扩容。接着挪动数据,在pos位置插入新的元素。
iterator insert(iterator pos, const T& x)
{assert(pos >= _start && pos <= _finish);//检查pos位置是否合理size_t len = pos - _start;//记录pos与开始位置差值if (_finish == _endofstorage)//满了要扩容{size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//防止迭代器失效}iterator end = _finish - 1;while (end >= pos)//挪动数据{*(end + 1) = *end;--end;}*pos = x;//插入新数据++_finish;return pos;
}
这里唯一要注意的是如果有发生扩容,pos的位置要进行更新。
2.5.3 pos位置删除
判断pos位置是否合理,然后挪动数据往前覆盖一个元素。
iterator erase(iterator pos)
{assert(pos >= _start && pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;
}
2.6 遍历访问
遍历访问有两种实现方式,一种是迭代器,前面已经写过了,另一种是方括号重载运算符,即我们常见的下标。
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}
const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}
2.7 全部代码
2.7.1 vector.h
#include <iostream>
#include <assert.h>
using namespace std;namespace yss
{template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//迭代器iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}//构造1vector(){}//构造2vector(size_t n, const T& x = T()){resize(n, x);}vector(int n, const T& x = T()){resize(n, x);}//构造3template<class InputIterator>vector(InputIterator first, InputIterator last){reserve(last - first);while (first != last){push_back(*first);++first;}}//拷贝构造//写法1//vector(const vector<T>& v)//{// _start = new T[v.capacity()];//开一样大的空间// memcpy(_start, v._start, sizeof(T) * v.size());//拷贝数据// _finish = _start + v.size();//// _endofstorage = _start + v.capacity();//}//写法2vector(const vector<T>& v){reserve(v.capacity());for (const auto& e : v){push_back(e);}}//赋值重载//写法1//vector<T>& operator=(const vector<T>& v)//{// if (this != &v)// {// T* tmp = new T[v.capacity()];//临时空间// memcpy(tmp, v._start, sizeof(T) * v.size());//拷贝数据// delete[] _start;// _start = tmp;// _finish = _start + v.size();//// _endofstorage = _start + v.capacity();// }// return *this;//}//写法2vector<T>& operator=(vector<T> v){swap(v);return *this;}//析构~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;//}}//返回个数size_t size() const{return _finish - _start;}//返回容量size_t capacity() const{return _endofstorage - _start;}//交换void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}//扩容void reserve(size_t n){if (n > capacity())//满了要扩容{size_t old = size();//记录当前元素个数T* tmp = new T[n];//新空间if(_start){//memcpy(tmp, _start, sizeof(T) * old);//将数据拷贝到新空间-浅拷贝for (size_t i = 0; i < old; i++){tmp[i] = _start[i];//深拷贝}delete[] _start;//释放就空间}_start = tmp;//指向新空间_finish = _start + old;//防止迭代器失效_endofstorage = _start + n;}}//改变元素个数void resize(size_t n, const T& x = T()){if (n <= size()){_finish = _start + n;}else{size_t len = n - size();if (n > capacity()){reserve(n);}while (len--){*_finish = x;++_finish;}}}//尾插void push_back(const T& x){if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}*_finish = x;++_finish;}//尾删void pop_back(){assert(size() > 0);--_finish;}//pos位置插入iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);size_t len = pos - _start;//记录pos与开始位置差值if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//防止迭代器失效}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}//pos位置删除iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}//下标T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}
2.7.2 test.cpp
#include"vector.h"int main()
{/*yss::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;*//*yss::vector<int> v(10);for (auto e : v){cout << e << " ";}cout << endl;*//*yss::vector<int> v1(5, 99);yss::vector<int> v2(v1.begin(), v1.end());for (auto e : v2){cout << e << " ";}cout << endl;*//*yss::vector<int> v1(5, 55);yss::vector<int> v2(v1);for (auto e : v2){cout << e << " ";}cout << endl;*//*yss::vector<int> v1(7, 44);yss::vector<int> v2 = v1;for (auto e : v2){cout << e << " ";}cout << endl;*//*yss::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);auto pos = find(v.begin(), v.end(), 2);v.insert(pos, 99);for (auto e : v){cout << e << " ";}*//*yss::vector<int> v;v.push_back(1);v.push_back(22);v.push_back(43);v.push_back(64);v.push_back(85);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;*/return 0;
}
相关文章:

【C++】vector的使用及模拟实现
目录 一、vector的介绍及使用1.1 介绍vector1.2 vector的使用1.2.1 构造1.2.2 遍历访问1.2.3 容量空间1.2.4 增删查改 二、vector的模拟实现2.1 成员变量2.2 迭代器相关函数2.3 构造-析构-赋值重载2.3.1 无参构造2.3.2 有参构造12.3.3 有参构造22.3.4 拷贝构造2.3.5 赋值重载2.…...

【数据库】sql优化有哪些?从query层面和数据库层面分析
目录 归纳sql本身的优化数据库层面的优化 归纳 这类型问题可以称为:Query Optimization,从清华AI4DB的paper list中,该类问题大致可以分为: Query RewriterCardinality EstimationCost EstimationPlan Optimization 从中文的角…...

nginx基本优化
安装nginx隐藏版本号 查看百度web服务器 [rootcjq11 ~]# curl -I http://www.baidu.com 隐藏nginx服务器版本号 [rootcjq11 ~]# cd /usr/local/src/nginx-1.22.0/ [rootcjq11 nginx-1.22.0]# vim src/core/nginx.h第13、14行修改版本号和服务器名称 [rootcjq11 nginx-1.2…...

软件测试|使用selenium处理单选框和多选框
简介 我们在web自动化测试工作中,经常会遇到对单选框(Radio Buttons)或者多选框(Checkboxes)进行操作的场景,单选框和多选框主要是用于我们做出选择或提交数据。本文将主要介绍selenium对于单选框和多选框…...
openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表
文章目录 openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表概述如何找到算法名称字符串列表?openssl-3.2.0\providers\implementations\include\prov\names.h备注END openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表 概述 进行加解密时, 先…...
vue3中的hook公共函数封装及运用
vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中,或者说是一些可以复用的公共方法/功能 使用Vue3的组合API封装的可复用的功能函数自定义hook的作用类似于vue2中的mixin技术自定义Hook…...

广州市工信局、天河区商务金融局及广州专精特新促进会走访思迈特
2024年1月11日下午,广州市工信局、天河区商务金融局及广州专精特新促进会相关负责人莅临广州思迈特软件总部调研指导,思迈特软件总裁兼COO姚诗成代表公司热情接待,并陪同调研。 调研组实地参观了思迈特软件,深入了解了思迈特发展历…...

vi编辑器显示行号和不显示行号切换命令
文章目录 1.临时生效只需要在vi编辑器里面输入1.1.显示行号1.2.不显示行号 2.永久生效 1.临时生效只需要在vi编辑器里面输入 1.1.显示行号 set number 或者 set nu如下图 1.2.不显示行号 set nonumber 或者 set nonu2.永久生效 首先打开配置文件/etc/vim/vimrc,向文件中添…...
使用 LLVM clang C/C++ 编译器编译 boost 基础框架类库
1、下载 boost 1.84 库的源代码放到待编译目录 2、解压并接入 boost 1.84 库源码的根目录 搜索默认的 clang 版本,WSL 2.0/Ubuntu 18.04 LTS 为 clang 6.x 执行命令: ./bootstrap.sh --with-toolsetclang ./b2 toolsetclang 另外一个方法比较麻烦需要…...

推荐一款.NET开发的物联网开源项目
物联网(IoT)是一个正在快速发展的技术领域,它涉及到各种设备、物体和系统的互联。所以各种物联网平台和物联网网关项目层出不穷,在物联网(IoT)领域,.NET平台扮演着重要的角色。作为一款广泛使用…...
正则表达式 (用于灵活匹配文本的表达式)
目录 . * . 用于匹配任意单个字符,除了换行符。 例如使用正则表达式 a.b, 它可以匹配aab、acb、a#b * 用于匹配前一个字符零次或多次。 例如,使用正则表达式 ab*c,它可以匹配 "ac"、"abc"、"abbc"&#…...

基于4G数采终端的供热管网在线监测方案
我国大部地区全面进入到冬季,北方各地已开启冬季供暖,以保障居民生活所需。由于城市化的发展,城市内各供热区域愈发分散、供热管道漫长、供热环境复杂,对于供热管网及换热站点的监测和维护提出了诸多挑战。 方案介绍 针对提高供热…...

OPC UA 开源库编译方法及通过OPC UA连接西门S7-1200 PLC通信并进行数据交换[一]
前言 在现代工业自动化领域,OPC UA(开放性生产控制和统一架构)是一种广泛应用的通信协议。本文将以通俗易懂的方式解释OPC UA的含义和作用,帮助读者更好地理解这一概念。 一、OPC UA的定义 OPC UA全称为“开放性生产控制和统一…...

2019年认证杯SPSSPRO杯数学建模B题(第二阶段)外星语词典全过程文档及程序
2019年认证杯SPSSPRO杯数学建模 基于统计和迭代匹配的未知语言文本片段提取模型 B题 外星语词典 原题再现: 我们发现了一种未知的语言,现只知道其文字是以 20 个字母构成的。我们已经获取了许多段由该语言写成的文本,但每段文本只是由字母…...

NFS的共享与挂载
一、NFS网络文件服务 1.1简介 NFS(Network File System 网络文件服务) 文件系统(软件)文件的权限 NFS 是一种基于 TCP/IP 传输的网络文件系统协议,最初由 Sun 公司开发。 通过使用 NFS 协议,客户机可以像访…...

函数式编程的Java编码实践:利用惰性写出高性能且抽象的代码
转载链接 本文会以惰性加载为例一步步介绍函数式编程中各种概念,所以读者不需要任何函数式编程的基础,只需要对 Java 8 有些许了解即可。 一 抽象一定会导致代码性能降低? 程序员的梦想就是能写出 “高内聚,低耦合”的代码&…...

2024年甘肃省职业院校技能大赛信息安全管理与评估 样题二 模块二
竞赛需要完成三个阶段的任务,分别完成三个模块,总分共计 1000分。三个模块内容和分值分别是: 1.第一阶段:模块一 网络平台搭建与设备安全防护(180 分钟,300 分)。 2.第二阶段:模块二…...
mysql 一对多 合并多个通过 逗号拼接展示
mysql 一对多 合并多个通过 逗号拼接展示 以上内容由chatgpt中文网 动态生成 SELECTuser_id,project_id,GROUP_CONCAT(project_specs_id) AS merged_specs_ids FROMzt_medication_specs_total WHEREspecs_num_total < 5 GROUP BYuser_id, project_id;laravel model 对应写…...

Java零基础教学文档第五篇:jQuery
今日新篇章 【jQuery】 【主要内容】 jQuery简介 jQuery安装 jQuery语法 jQuery选择器 jQuery事件处理 jQueryDOM操作 jQuery元素遍历 jQuery过滤 jQuery其它方法 【学习目标】 1.jQuery简介 1.1 jQuery简介 jQuery 库可以通过一行简单的标记被添加到网页中。 1.…...
samba
samba 文章目录 samba1. samba简介2. samba访问3. 示例 1. samba简介 Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成。 在此之前我们已经了解了NFS,NFS与samba一样,也是在网络中实现文件共享的一种实现&a…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...