STL - vector的使用和模拟实现
目录
一:vector的构造函数
二:vector的迭代器
三vector的空间增长问题
四:vector 增删查改接口
五:vector的模拟实现
(一)一些简单接口的实现:
(二)一些复杂接口的实现
迭代器失效问题:
(三)五个构造函数的模拟实现
(1)默认构造
(2)拷贝构造函数
(3)使用n个val去初始化容器的构造
(4)迭代器区间初始化构造:
(5)赋值构造
vector其实就是我们数据结构中的顺序表。
一:vector的构造函数
(1)无参构造:即默认构造。vector构造一个没有元素的空容器。
// 走默认构造vector<char> vc;
(2)填充构造函数:构造一个包含 n 个元素的容器。每个元素都是 val 的副本。
// 默认初始化为0vector<int> v1(10);// 初始化为1vector<int> v2(10, 1);
(3)拷贝构造函数:构造一个容器,其中包含与范围 [first,last] 一样多的元素,每个元素都按相同的顺序从该区域中的相应元素构造。
// 拷贝构造vector<int> v3(v2);
(4)使用迭代器进行初始化的构造:构造一个容器,其中包含与范围 [first,last] 一样多的元素,每个元素都按相同的顺序从该区域中的相应元素构造。
vector<int> v1(10);// 使用迭代器进行初始化vector<int> v3(v1.begin(), v1.end()-3);
二:vector的迭代器
同其他的string容器一样,begin和end分别是容器的第一个位置和最后一个位置,反向迭代器则相反。const迭代器和非const迭代器和string的也是一样的C++_STL_string使用和模拟实现-CSDN博客
这里可以看一下。
// 初始化为一个定值
vector<int> v2(10, 1);
// 迭代器使用
//vector<int>::iterator it;
//vector<int>::const_iterator it;
// 反向迭代器
vector<int>::reverse_iterator it;
//it = v2.begin();
it = v2.rbegin();
//while (it != v2.end())
while (it != v2.rend())
{//cout << ++(*it) << " ";cout << *it << " ";it++;
}
三vector的空间增长问题
(1)size():返回元素的实际个数
(2)capacity():容器容量的大小
(3)resize() :修改实际容器元素个数
(4)reserve():改变capacity的大小
(5)empty():判断容器是否为空
注意:在g++和vs中capacity在扩容的时候可能不一样,有的是1.5倍扩容,有的是2倍扩容
具体我们来看下这几个接口的情况:
// 测试vector 空间增长问题接口
void test02()
{vector<int> v2(10, 1);cout << v2.size() << endl;cout << v2.capacity() << endl;cout << v2.empty() << endl;// resize如果n大于size的话会将size的元素进行初始化v2.resize(40);// 这里改变capacity的为13并不会缩容v2.reserve(13);cout << v2.capacity() << endl;for (auto& x : v2){cout << x << " ";}
}
四:vector 增删查改接口
(1)push_back:在容器末尾插入一个值
void push_back (const value_type& val);
(2)pop_back:删除容器末尾的一个值
void pop_back();
(3)find:在范围内查找值
返回范围中比较等于 的第一个元素的迭代器。如果未找到此类元素,则函数返回 。
template <class InputIterator, class T> InputIterator find (InputIterator first, InputIterator last, const T& val);
(4)insert:通过在指定位置的元素之前插入新元素来扩展向量,从而有效地增加容器大小(增加插入的元素数)。
single element (1)
iterator insert (iterator position, const value_type& val);
fill (2) void insert (iterator position, size_type n, const value_type& val);
range (3)
template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);
(5)erase:从向量中删除单个元素 (位置) 或一系列元素 ([first,last))
iterator erase (iterator position);iterator erase (iterator first, iterator last);
(6)swap:交换向量的数据空间
void swap (vector& x);
(7)operator[]:返回对向量容器中位置 n 处的元素的引用。
reference operator[] (size_type n);const_reference operator[] (size_type n) const;
1~7接口代码:
void test03()
{//find
//push_back
///vector<int> v2;for (int i = 0; i < 100; i++){v2.push_back(i);}v2.pop_back();//for (auto& e : v2)//{// cout << e << " ";//}cout << endl;// 在一个范围内查找数据val,并且返回第一个等于val的迭代器,如果没有找到就返回区间的末尾迭代器vector<int>::iterator it = find(v2.begin(),v2.end(), 31);//while (it != v2.end())//{// //cout << ++(*it) << " ";// cout << *it << " ";// it++;//}
/
//insert
//在pos迭代器位置插入val,不是下标v2.insert(v2.begin() + 10, 10025);it = v2.begin();//while (it != v2.end())//{// //cout << ++(*it) << " ";// cout << *it << " ";// it++;//}//erase
//删除迭代区间的数据或者迭代pos位置的数据// iterator erase (iterator first, iterator last);v2.erase(v2.begin() + 3, v2.end() - 10);it = v2.begin();while (it != v2.end()){//cout << ++(*it) << " ";cout << *it << " ";it++;}// iterator erase (iterator position);v2.erase(v2.begin() + 20);it = v2.begin();while (it != v2.end()){//cout << ++(*it) << " ";cout << *it << " ";it++;}
}// 测试swap
void test04()
{// template <class T, class Alloc>// void swap(vector<T, Alloc>&x, vector<T, Alloc>&y);vector<int> v2(10, 1);vector<int> v3(10, 3);// 交换两个向量的空间v2.swap(v3);vector<int>::iterator it;it = v2.begin();//while (it != v2.end())//{// //cout << ++(*it) << " ";// cout << *it << " ";// it++;//}
//
//reference operator[] (size_type n);const_reference operator[] (size_type n) const;for (int i = 0; i < v2.size(); i++){cout << ++v2[i]<< ' ';}}
五:vector的模拟实现
在实现vector的时候我们采用模板来实现,方便vector存取任意的类型,可以是自定义类型,也可以是内置类型,例如:vector<vector<int>>(二维数组)。
因为vector的数据结构本质是顺序表,所以我们可以设置一个顺序表来模拟实现它。
然后设置三个标志位:
private:iterator _start = nullptr;iterator _finsh = nullptr;iterator _end_of_storage = nullptr;
_start表示容器的起始位置,_finsh表示有效数据个数的最后一个位置,_end_of_storage表示容器容量的大小。
iterator使我们使用typedef将模板类型重命名的。
namespace cp
{template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start = nullptr;iterator _finsh = nullptr;iterator _end_of_storage = nullptr;};
}
我们将我们的vector使用一个单独的命名空间CP包起来,避免跟库里面的vector冲突。
关于模板请看:C++模板初阶-CSDN博客
(一)一些简单接口的实现:
一些比较简单的接口我们就将他们放到类里面
关于容量相关的接口:
//清除数据
void clear()
{_finsh = _start;
}
//判断容器是否为空
bool empty()const
{return _finsh == _start;
}
//获取容器容量的大小
size_t size() const
{//指针相减得到元素个数return _finsh - _start;
}
//获取容器容量的大小
size_t capacity() const
{return _end_of_storage - _start;
}
迭代器的实现:const迭代器和非const迭代器,反向迭代器就不实现了
几种迭代器
iterator begin()
{return _start;
}
iterator end()
{return _finsh;
}
const_iterator cbegin() const
{return _start;
}
const_iterator begin() const
{return _start;
}
const_iterator end() const
{return _finsh;
}
const_iterator cend() const
{return _finsh;
}
函数后边加const表示成员变量不可以修改。
方括号下标[ ]的重载实现:
//返回普通引用,可以修改
T& operator[](size_t pos)
{return _start[pos];
}
//返回const引用,不可以修改
const T& operator[](size_t pos)const
{return _start[pos];
}
尾删除函数
void pop_back()
{assert(!empty());--_finsh;
}
(二)一些复杂接口的实现
这些复杂的接口我们将他们放到类外面。
(1)reserve:开辟空间并检查。
template<class T>
void vector<T>::reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];for (int i = 0; i < old_size; i++){tmp[i] = _start[i];}if (_start){delete[] _start;}_start = tmp;_finsh = _start + old_size;_end_of_storage = _start + n;}
}
注意:在更新成员变量的时候要先预存一份没交换之前的size,因为在_start和tmp交换的时候也会影响到size的改变。另外,记得释放旧空间,当然如果原来的容器是空的就不用释放了。
(2)push_back:在容器的末尾位置尾插一个数据
template<class T>
void vector<T>::push_back(const T& x)
{if (_finsh == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finsh = x;++_finsh;
}
只要是往容器里面插入数据都要检查容器是否满了,如果满了就需要先扩容再插入数据,这和数据结构的顺序表是一样的。
迭代器失效问题:
通常我们在插入和删除的时候使用的是迭代器,所以在一些情况下会导致迭代器失效,string的迭代器版本也会有这样的情况。
(1)insert:在pos迭代器位置插入数据,并且返回当前插入数据位置的迭代器。
template<class T>typename vector<T>::iterator vector<T>::insert(iterator pos, T val){assert(pos >= _start);assert(pos < _finsh);if (_finsh == _end_of_storage){//防止pos位置因为扩容而失效size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finsh;while (end >= pos){*end = *(end - 1);end--;}*pos = val;_finsh++;pos++;return pos;}
知识点补充:在这里我们使用typename的原因,因为在实例化的时候编译器不知道迭代器(iterator)到底是类型还是成员变量,所以我们使用typename。(规定使用typename指定的是类型)。
在实现insert的时候我们要先扩容,但是扩容的时候原来_start指向的空间和tmp交换了
但是我们的pos位置迭代器还是原来空间的pos,原来空间交换给tmp之后随着函数结束tmp变量指向的空间销毁,pos所指向的也就是一个随机空间。当我们在挪动数据的时候,因为pos不是当前对象指向的空间位置,所以会发生一些错误,插入数据也就会导致一些问题。
解决方案:
如上述代码,我们先记录pos的相对位置,在更新_start指向的空间的之后我们也更新pos的迭代器,这样就不会发生问题了。
这是第一种迭代器失效的情况:迭代器指向的空间被释放,类似于野指针。
(2)erase:挪动覆盖删除迭代器位置的数据,返回pos位置的迭代器。
我们先实现一下这个成员函数,同样,我们将他们放到类外面。
template<class T>
typename vector<T>::iterator vector<T>::erase(iterator pos)
{assert(pos >= _start);assert(pos < _finsh);iterator start = pos;while (start < _finsh){*start = *(start + 1);start++;}_finsh--;return pos;
}
当我们删除数据之后,pos还是指向原来的位置,但是如果我们以下标的形式看待它。
例如:我们来测试一下,我们写一个vector,往里面放一些数据,删除之后使用它的迭代器来操作。
因为方便测试我们实现一个打印容器的函数模板:
template<class Container>void print_container(const Container& con){/*auto it = con.cbegin();while (it != con.cend()){cout << *it << " ";++it;}cout << endl;*/for (auto& e : con){cout << e << " ";}cout << endl;}
//测试迭代器失效问题
void test_vaid_iterator()
{cp::vector<int> v;v.push_back(0);v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//删除5位置的内容cp::vector<int>::iterator it = v.erase(v.begin()+3);cp::print_container(v);*it *= 10;cp::print_container(v);
}
可以看到原来位置的意义已经改变了,我们原本的位置在删除数据之后已经改变了。
(3)库里面的迭代器失效问题:Windows平台下。
我们来测试一下库里面的insert和erase。
测试函数:
(1)测试insert:
//测试迭代器失效问题
void test_vaid_iterator()
{std::vector<int> v;v.push_back(0);v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//删除5位置的内容std::vector<int>::iterator pos = v.begin() + 3;v.insert(pos, 3);//删除pos位置的内容cp::print_container(v);//访问pos位置*pos*=10;cp::print_container(v);
}
(2)的是erase:
//测试迭代器失效问题
void test_vaid_iterator()
{std::vector<int> v;v.push_back(0);v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//删除5位置的内容std::vector<int>::iterator pos = v.begin() + 3;cp::print_container(v);v.erase(pos);//删除pos位置的内容*pos *= 10;cp::print_container(v);
}
在vs2020中编译器会强制检查报错。在insert和erase之后是不允许访问pos位置的迭代器的。
总结:不管是自己实现的还是标准库内部实现的,在容器中(string,vector..)为了安全考虑,在insert和erase之后就不要访问pos位置的迭代器了。如果要访问就要更新迭代器。
(三)五个构造函数的模拟实现
接下来我们来实现他们的成员函数。
(1)默认构造
因为构造函数默认走初始化列表,所以我们在实现默认构造函数的时候可以这样写。
vector() = default;
因为我们在成员变量哪里给了缺省值,所以使用default关键字可以强制编译器生成默认构造。
当然,如果们没在成员变量给缺省参数的话可以自己走初始化列表:
vector():_start(nullptr),_finsh(nullptr),_end_of_storage(nullptr){// 啥也不干,因为会自动走舒适化列表用缺省值完成初始化}
我们来测试一下:
可以看到v对象的三个成员已经被初始化完成了,都是空指针。
(2)拷贝构造函数
用一个已经存在的对象去初始化另一个刚创建的对象
template<class T>
vector<T>::vector(const vector<T>& v)
{//防止自己给自己赋值if (this != &v){reserve(v.capacity());for (auto& e : v){push_back(e);}//_finsh和_endd_of_storage是指针,不是整型,不能直接赋值,而且在reserve和push_back的时候已经更新过了,不用更新//_finsh = v._finsh;//_end_of_storage = v._end_of_storage;}
}
(3)使用n个val去初始化容器的构造
template<class T>vector<T>::vector(size_t n, vector<T>& val){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}
(4)迭代器区间初始化构造:
template<class input_iterator>
vector(input_iterator first, input_iterator last)
{while (first != last){push_back(*first);first++;}
}
测试:
void Test03()
{cp::vector<int> v;v.push_back(0);v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cp::print_container(v);cp::vector<int> v4(v.begin()+2, v.begin() + 5);cp::print_container(v4);}
(5)赋值构造
因为都是自定义类型,且需要开辟空间,所以和拷贝构造一样都是深拷贝。
//this = vvector<T>& operator=(const vector<T>& v){if (this != &v){clear();reserve(v.size());/*size_t size = v.size();T* tmp = new T[v.capacity()];for (size_t i = 0; i < size; i++){tmp[i] = v._start[i];}if (_start)delete[] _start;_start = tmp;_finsh = _start + size;_end_of_storage = _start + v.capacity();*/for (auto& e : v){push_back(e);}}return *this;}
赋值构造的现代写法:
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finsh, v._finsh);std::swap(_end_of_storage, v._end_of_storage);
}
//现代写法
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}
(6)析构函数
~vector()
{if (_start)//如果_start是空(为假),则说明已经析构了,不需要析构{delete[] _start;_start = _finsh = _end_of_storage = nullptr;}
}
相关文章:

STL - vector的使用和模拟实现
目录 一:vector的构造函数 二:vector的迭代器 三vector的空间增长问题 四:vector 增删查改接口 五:vector的模拟实现 (一)一些简单接口的实现: (二)一些复杂接口的…...

《鸿蒙生态:开发者的机遇与挑战》
一、引言 在当今科技飞速发展的时代,操作系统作为连接硬件与软件的核心枢纽,其重要性不言而喻。鸿蒙系统的出现,为开发者带来了新的机遇与挑战。本文将从开发者的角度出发,阐述对鸿蒙生态的认知和了解,分析鸿蒙生态的…...

【C++融会贯通】二叉树进阶
目录 一、内容说明 二、二叉搜索树 2.1 二叉搜索树概念 2.2 二叉搜索树操作 2.2.1 二叉搜索树的查找 2.2.2 二叉搜索树的插入 2.2.3 二叉搜索树的删除 2.3 二叉搜索树的实现 2.3.1 二叉搜索树的节点设置 2.3.2 二叉搜索树的查找函数 2.3.2.1 非递归实现 2.3.2.2 递…...
使用python-Spark使用的场景案例具体代码分析
使用场景 1. 数据批处理 • 日志分析:互联网公司每天会产生海量的服务器日志,如访问日志、应用程序日志等。Spark可以高效地读取这些日志文件,对数据进行清洗(例如去除无效记录、解析日志格式)、转换(例如…...
如何查看本地的个人SSH密钥
1.确保你的电脑上安装了 Git。 你可以通过终端或命令提示符输入以下命令来检查: git --version 如果没有安装,请前往 Git 官网 下载并安装适合你操作系统的版本。 2.查找SSH密钥 默认情况下,SSH密钥存储在你的用户目录下的.ssh文件夹中。…...
本人认为 写程序的三大基本原则
1. 合法性 定义:合法性指的是程序必须遵守法律法规和道德规范,不得用于非法活动。 建议: 了解法律法规:在编写程序之前,了解并遵守所在国家或地区的法律法规,特别是与数据隐私、版权、网络安…...

A030-基于Spring boot的公司资产网站设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...
React Hooks 深度解析与实战
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 React Hooks 深度解析与实战 React Hooks 深度解析与实战 React Hooks 深度解析与实战 引言 什么是 Hooks? 定义 为什么需要 Ho…...

#渗透测试#SRC漏洞挖掘#蓝队基础之网络七层杀伤链04 终章
网络杀伤链模型(Kill Chain Model)是一种用于描述和分析网络攻击各个阶段的框架。这个模型最初由洛克希德马丁公司提出,用于帮助企业和组织识别和防御网络攻击。网络杀伤链模型将网络攻击过程分解为多个阶段,每个阶段都有特定的活…...

计算机毕业设计Python+大模型农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

爬虫补环境案例---问财网(rpc,jsdom,代理,selenium)
目录 一.环境检测 1. 什么是环境检测 2.案例讲解 二 .吐环境脚本 1. 简介 2. 基础使用方法 3.数据返回 4. 完整代理使用 5. 代理封装 6. 封装所有使用方法 jsdom补环境 1. 环境安装 2. 基本使用 3. 添加参数形式 Selenium补环境 1. 简介 2.实战案例 1. 逆向目…...
SpringBoot有几种获取Request对象的方法
HttpServletRequest 简称 Request,它是一个 Servlet API 提供的对象,用于获取客户端发起的 HTTP 请求信息。例如:获取请求参数、获取请求头、获取 Session 会话信息、获取请求的 IP 地址等信息。 那么问题来了,在 Spring Boot 中…...
在 Windows 11 中使用 MuMu 模拟器 12 国际版配置代理
**以下是优化后的教学内容,使用 Markdown 格式,便于粘贴到 CSDN 或其他支持 Markdown 格式的编辑器中: 在 Windows 11 中使用 MuMu 模拟器 12 国际版配置代理 MuMu 模拟器内有网络设置功能,可以直接在模拟器中配置代理。但如果你不确定如何操作,可以通过在 Windows 端设…...

ASP.NET Core Webapi 返回数据的三种方式
ASP.NET Core为Web API控制器方法返回类型提供了如下几个选择: Specific type IActionResult ActionResult<T> 1. 返回指定类型(Specific type) 最简单的API会返回原生的或者复杂的数据类型(比如,string 或者…...
SQL面试题——蚂蚁SQL面试题 连续3天减少碳排放量不低于100的用户
连续3天减少碳排放量不低于100的用户 这是一道来自蚂蚁的面试题目,要求我们找出连续3天减少碳排放量低于100的用户,之前我们分析过两道关于连续的问题了 SQL面试题——最大连续登陆问题 SQL面试题——球员连续四次得分 这两个问题都是跟连续有关的,但是球员连续得分的难…...

Python酷库之旅-第三方库Pandas(216)
目录 一、用法精讲 1011、pandas.DatetimeIndex.tz属性 1011-1、语法 1011-2、参数 1011-3、功能 1011-4、返回值 1011-5、说明 1011-6、用法 1011-6-1、数据准备 1011-6-2、代码示例 1011-6-3、结果输出 1012、pandas.DatetimeIndex.freq属性 1012-1、语法 1012…...
论文解析:计算能力资源的可信共享:利益驱动的异构网络服务提供机制
目录 论文解析:计算能力资源的可信共享:利益驱动的异构网络服务提供机制 KM-SMA算法 KM-SMA算法通过不断更新节点的可行顶点标记值(也称为顶标),利用匈牙利方法(Hungarian method)来获取匹配结果。在获取匹配结果后,该算法还会判断该结果是否满足Pareto最优性,即在没…...

Spring AOP技术
1.AOP基本介绍 AOP 的全称 (aspect oriented programming) ,面向切面编程。 1.和传统的面向对象不同。 面向切面编程是根据自我的需求,将切面类的方法切入到其他的类的方法中。(这么说抽象吧!来张图来解释。) 如图 传…...

数字IC实践项目(10)—基于System Verilog的DDR4 Model/Tb 及基础Verification IP的设计与验证(付费项目)
数字IC实践项目(10)—基于System Verilog的DDR4 Model/Tb 及基础Verification IP的设计与验证(付费项目) 前言项目框图1)DDR4 Verification IP2)DDR4 JEDEC Model & Tb 项目文件1)DDR4 Veri…...
MATLAB保存多帧图形为视频格式
基本思路 在Matlab中,要将drawnow绘制的多帧数据保存为视频格式,首先需要创建一个视频写入对象。这个对象用于将每一帧图像数据按照视频格式的要求进行组合和编码。然后,在每次drawnow更新绘图后,将当前的图形窗口内容捕获为一帧图…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

【threejs】每天一个小案例讲解:创建基本的3D场景
代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone,无需安装依赖,直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景(Scene) 使用 THREE.Scene(…...