【C++】手撕vector(vector的模拟实现)

手撕vector目录:
一、基本实现思路方针
二、vector的构造函数剖析(构造歧义+拷贝构造)
2.1构造函数使用的歧义问题
2.2 vector的拷贝构造和赋值重载(赋值重载不是构造哦,为了方便写在一起)
三、vector的基本接口
3.1empty和clear
3.2 size和capacity
3.3 [ ]和iterator
四、 resize和reserve
五、尾插尾删
六、迭代器失效
6.1 insert
6.2 erase
七、vector.h
一、基本实现思路方针
本篇的目的很简单,只有一个:模拟实现vector
如何去模拟实现?我们可以看看vector的源码,我们可以抽离出主体框架:
namespace lzy//防止命名冲突
{template<class T>class lzy_vector{public:typedef T* iterator;typedef const T* const_iterator;public://成员函数private:T* _start;T* _finish;T* _end_of_storage;}
}

对于size = _finish - _start
对于capacity = _endofstorage-_start
可以看到,vector 的底层和 string 一样,都是一个指针指向一块动态开辟的数组,但是二者不同的是,string 是用 T* str 和 _size 和 _capacity 三个成员变量来维护这块空间,而 vector 是用 _finish 和 _end_of_storage 两个指针来维护这块空间;虽然 vector 使用指针看起来难了一些,但本质上其实是一样的 : _size = _finish - _start, _capacity = _end_of_storage - _start;
有了这些作为铺垫,我们对于vector的模拟实现大概有了一个基本的框架,话不多说,直接进入主题👇
二、vector的构造函数剖析(构造歧义+拷贝构造)

1. 无参的构造函数,我们利用初始化列表来进行初始化。用nullptr初始化比较好,因为nullptr的free或delete都不会出错
2. 另一种构造是用n个value值来进行构造,value既有可能是内置类型,也有可能是自定义类型,所以如果用引用作参数的话,需要用const引用,也就是常引用来作参数,否则无法接收内置类型的实参
3. 除无参构造外,常用的构造还有迭代器区间作为参数的构造函数。这里的迭代器需要用函数模板来实现,因为构造vector所用的迭代器不一定只是vector类型的,还有可能是string类型,所以这里的迭代器形参需用模板来实现
lzy_vector() // 初始化列表给空指针:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){}//n个val构造lzy_vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);for (size_t i = 0; i < n; i++)push_back(val);}//迭代器区间构造template<class InputIterator>lzy_vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){while (first != last){push_back(*first);++first;}}
吐槽:经过一番折腾,总算是有了个输出结果,要不是reserve没写,要不就是push_back没写,要不就是类模板没搞,要不就是迭代器输出的时候迭代器的begin没写。。。。
2.1构造函数使用的歧义问题
1. 在实现完n个value构造的构造函数之后,如果我们此时用10个int类型的数字1来构造对象v1,实际会报错,报错的原因其实是由于函数的匹配优先级所导致的实参无法正确匹配相应的构造函数。而使用10个char类型的字符A却不会报错,这其实也是由于函数的匹配优先级决定的
2. 对于size_t和常引用作为参数的构造来说,它的匹配优先级对于10个1实际不是最高的,因为常引用需要进行类模板参数T类型的推导,而10又是整型int,int到size_t还需要进行隐式类型转换,代价有点大
而对于迭代器区间作为参数的构造来讲,函数模板参数InputIterator只需要进行一次类型推导即可完成匹配,所以用10个1来构造时,实际匹配的构造函数是迭代器区间作为参数的构造函数,而在匹配的构造函数中,对迭代器区间进行了解引用,那就是对常量10进行了解引用,则发生非法的间接寻址(用了迭代器区间的构造函数,会报错)3 对于这种问题的解决,可以将size_t换成int类型,或者将10强转为size_t类型,但stl源码的解决方式并非是这样的,而是利用了函数重载来解决了这个问题,多重载了一个类型为int的构造函数


好的,我们继续开干!上面的代码运行结果正确

解决方法:

2.2 vector的拷贝构造和赋值重载(赋值重载不是构造哦,为了方便写在一起)
1. 对于拷贝构造的实现,和string一样,还是有传统写法和利用打工人的现代写法,利用打工人的本质实际就是代码重构。传统写就是提前用reserve预留好空间,然后push_back将数据尾插到提前预留的空间当中(这个逻辑还是非常清楚的)

这里让我想起来一个知识点:那会vector介绍接口的时候,我们存在越界访问的问题,我们在reserve之后, 利用[ ] 进行赋值,这样是不对的,因为我们只有capacity,没有size。但是利用push_back插入数据的时候,会进行检查:如果size为0,那么就会赋值,所以reserve之后push_back是合理的

注意:拷贝构造这么写是不对的(我本以为是我的接口写的不完善,但是在std中运行发现也是报错)
原因:

而使用拷贝构造的时候必须这样:
void testvector()
{lzy::lzy_vector<int> v1(10,1);lzy::lzy_vector<int> v2(v1);for(auto e : v2){cout << e << " ";}
}
int main()
{testvector();return 0;
}
2. 无论是从代码可读性还是实用性的角度来讲,现代写法都更胜一筹,这里利用形参对象v的迭代器来构造临时对象tmp,然后将对象tmp和* this进行交换,为了防止对象tmp离开函数栈帧销毁时造成野指针的访问问题,所以在调用构造函数时采用了初始化列表的方式将* this的三个成员都初始化为nullptr(原先埋下的伏笔,居然在这里展现出来了)
注:别忘了重新写swap函数
lzy_vector(const lzy_vector<T>& v):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){vector<T> tmp(v.begin(), v.end()); //利用了迭代器模板swap(tmp);}


3. 在实现拷贝构造后,实现赋值重载就比较简单了,利用传值拷贝构造的临时对象即可,然后调用swap类成员函数即可完成自定义类型的赋值工作。为了符合连续赋值含义,我们利用引用来作为返回值。
lzy_vector<T>& operator=(lzy_vector<T> v) //复用拷贝构造,存在自我赋值的问题,但不影响程序正确性{swap(v);return *this;}
注意:参数不可以加引用,正常来说,v1=v2; 如果加上引用的话,v2就会变成v1的值
总结:由于拷贝构造必须有引用,所以函数体内另外找了一个打工人,但我们的赋值不需要,所以引用和tmp必须同时存在,这样才可以满足我们的逻辑
三、vector的基本接口
3.1empty和clear
empty
bool empty() const
{return _finish == _start;
}
clear
void clear()
{_finish = _start;//这里可不是置为nullptr哦
}
3.2 size和capacity
size
size_t size() const
{return _finish - _start;
}
capacity
size_t capacity() const
{return _endofstorage - _start;
}
3.3 [ ]和iterator
[ ]
提供const版本和非const版本:
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos)const
{assert(pos < size());return _start[pos];
}
iterator
同理普通迭代器和const迭代器版本,同理,范围for循环此时也是可以实现的:
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;}
四、 resize和reserve
这两个接口需要单独拎出来,这是因为后面的插入等相关操作需要用到,所以我们先来看看这两个接口,同时这里有一些问题值得我们去注意:
resize
n个数据去初始化,这个n是多大,会造成什么影响?我们需要进行分类讨论:
//分情况
void resize(size_t n, const T& val = T())
{if (n > capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}
}
reserve
void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];//size()需要先保存起来,后面_start会发生改变size_t sz = size();//为空不需要拷贝了if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝//delete[] _start;}_start = tmp;_finish = _start+sz;_endofstorage = _start + n;}
}
五、尾插尾删
void push_back(const T& x)
{if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish = x;++_finish;
}void pop_back()
{assert(_finish > _start);--_finish;
}
六、迭代器失效
6.1 insert
//迭代器失效:扩容引起野指针问题
void insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;
}
测试代码:
void Test3()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;
}

这是因为扩容导致pos失效了:

insert过程中发生扩容,导致it指向的空间实际上已经被释放,it指向已被释放的空间是野指针,造成了迭代器失效
所以,我们应该去更新pos,算出pos刚开始的相对位置,然后再去进行更新即可解决问题。但是此时外面调用insert的it仍然是失效的,因为是传值调用,形参改变不影响实参,可以通过返回值接收解决问题。(如果是传引用的话,只能传变量,而临时对象具有常性,不能调用,存在很多问题),所以直接用返回值解决。
改正代码:
iterator insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){//扩容会导致pos迭代器失效,需要更新size_t len = pos - _start;size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}
6.2 erase
挪动数据进行覆盖即可:
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;
}
erase的pos也可能会导致pos失效,测试代码:
void Test6(){//删除所有偶数vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}++it;}for (auto e : v){cout << e << " ";}cout << endl;}
测试发现:
1,2,3,4的时候发生崩溃
1,2,2,3,5结果只删了一个2
1,2,3,4,5结果是正常的
上述代码在VS下,当erase(it)之后,it指向的位置发生改变,然后在++it的话,会出现问题,出现一些错误,造成迭代器失效。
我们最好统一认为失效了。
正确的erase:
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;
}
测试代码:
void Test6(){//删除所有偶数vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}
七、vector.h
#include<iostream>
using namespace std;
namespace lzy
{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;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}/*vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_endofstorage(nullptr){reserve(v.capacity());for (const auto& e : v){push_back(e);}}*///vector<int> v1(10, 5);//vector<char> v2(10, 'A');vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}//改成int或强转vector(int n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}template <class InputIterator>vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);//int不能解引用++first;}}vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){vector<T> tmp(v.begin(), v.end());swap(tmp);}//缺陷:自己拷贝自己vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t sz = size();//为空不需要拷贝了if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝//delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}//分情况void resize(size_t n, const T& val = T()){if (n > capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}}bool empty() const{return _finish == _start;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void push_back(const T& x){if (_finish == _endofstorage){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish = x;++_finish;}void pop_back(){assert(_finish > _start);--_finish;}//迭代器失效:野指针问题/*void insert(iterator pos, const T& val){assert(pos >= _start);assert(pos < _finish);if (_finish == _endofstorge){reserve(capacity() == 0 ? 4 : capacity() * 2);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;}*/iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){//扩容会导致pos迭代器失效,需要更新size_t len = pos - _start;size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}void clear(){_finish = _start;}public:iterator _start;iterator _finish;iterator _endofstorage;};void Test1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;}void Test2(){vector<int> v;v.resize(10, -1);for (auto e : v){cout << e << " ";}cout << endl;v.resize(5);for (auto e : v){cout << e << " ";}cout << endl;v.pop_back();v.pop_back();v.pop_back();v.pop_back();v.pop_back();v.pop_back();}void Test3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;v.insert(v.end(), 0);for (auto e : v){cout << e << " ";}cout << endl;vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;}void Test4(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){//传值v.insert(it, 30);}//insert以后it不能使用,可能迭代器失效(野指针)//(*it)++;for (auto e : v){cout << e << " ";}cout << endl;}void Test5(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.erase(it);}cout << *it << endl;// (*it)++;for (auto e : v){cout << e << " ";}cout << endl;}void Test6(){//删除所有偶数vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}void Test7(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int> v1(v);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2;v2.push_back(10);v2.push_back(20);v2.push_back(30);v1 = v2;for (auto e : v1){cout << e << " ";}cout << endl;v1 = v1;for (auto e : v1){cout << e << " ";}cout << endl;}void Test8(){string str("hello world");vector<int> v(str.begin(), str.end());for (auto e : v){cout << e << " ";}cout << endl;/*vector<int> v1(v.begin(), v.end());*/vector<int> v1(10, 5);//vector<char> v2(10, 'A');for (auto e : v1){cout << e << " ";}cout << endl;}class Solution {public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv;vv.resize(numRows);for (size_t i = 0; i < vv.size(); i++){vv[i].resize(i + 1, 0);vv[i][0] = vv[i][vv[i].size() - 1] = 1;}for (size_t i = 0; i < vv.size(); i++){for (size_t j = 0; j < vv[i].size(); j++){if (vv[i][j] == 0){vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}}return vv;}};void Test9(){vector<vector<int>> vvRet = Solution().generate(5);for (size_t i = 0; i < vvRet.size(); i++){for (size_t j = 0; j < vvRet[i].size(); j++){cout << vvRet[i][j] << " ";}cout << endl;}/*vector<vector<int>> vv;vector<int> v(10, 1);vv.push_back(v);vv.push_back(v);vv.push_back(v);vv.push_back(v);vv.push_back(v);for (size_t i = 0; i < vv.size(); i++){for (size_t j = 0; j < vv[i].size(); j++){cout << vv[i][j] << " ";}cout << endl;}*/cout << endl;}void Test10(){vector<string> v;v.push_back("11111111111111111111");v.push_back("22222222222222222222");v.push_back("33333333333333333333");v.push_back("44444444444444444444");v.push_back("55555555555555555555");for (auto e : v){cout << e << " ";}cout << endl;}
}
希望对大家有所帮助!
相关文章:
【C++】手撕vector(vector的模拟实现)
手撕vector目录: 一、基本实现思路方针 二、vector的构造函数剖析(构造歧义拷贝构造) 2.1构造函数使用的歧义问题 2.2 vector的拷贝构造和赋值重载(赋值重载不是构造哦,为了方便写在一起) 三、vector的…...
智能指针那些事
《Effective Modern C》学习笔记之条款二十一:优先选用std::make_unique和std::make_shared,而非直接new - 知乎...
Fiddler抓取手机https包的步骤
做接口测试时,有时我们需要使用fiddler进行抓包分析,那么如何抓取https包。主要分为以下七步: 1.设置fiddler选项:Tools->Options,按如下图勾选 2.下载并安装Fiddler证书生成器 下载地址:http://www.telerik.com/…...
idea没有maven工具栏解决方法
背景:接手的一些旧项目,有pom文件,但是用idea打开的时候,没有认为是maven文件,所以没有maven工具栏,不能进行重新加载pom文件中的依赖。 解决方法:选中pom.xml文件,右键 选择添加为…...
levelDB引擎
一、背景 1.1、影响磁盘性能的因素: 主要受限于磁盘的寻道时间,优化磁盘数据访问的方法是尽量减少磁盘的IO次数。磁盘数据访问效率取决于磁盘IO次数,而磁盘IO次数又取决于数据在磁盘上的组织方式。磁盘数据存储大多采用B树类型数据结构&…...
IM同步服务
设计概述 后台同步方案的设计就是数据存储结构的设计,如何快速体现“信息变化”,如何快速计算出“变化信息”。后台数据存储结构是由同步协议中同步契约决定的。 设计方案 该方案的同步是按照业务粒度来划分,只需要同步sdk要求同步的数据。…...
MySQL 运维常用脚本
常用功能脚本 1.导出整个数据库 mysqldump -u 用户名 -p –default-character-setlatin1 数据库名 > 导出的文件名(数据库默认编码是latin1) mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql 2.导出一个表 mysqldump -u 用户名 -p 数据库名 表名> 导出的文件…...
ABC322刷题记
ABC322刷题记 T1.A A - First ABC 2。 妥妥的简单题…… 用find函数做就行。(如果不存在那个子串就返回-1,否则返回第一次出现位置) 注意题目中编号是从1开始的。 时间复杂度:O(log(n))。find函数有一定代价,我记…...
visual studio的安装及scanf报错的解决
visual studio是一款很不错的c语言编译器 下载地址:官网 点击后跳转到以下界面 下滑后点击下载Vasual Sutdio,选择社区版即可 选择位置存放下载文件后,即可开始安装 安装时会稍微等一小会儿。然后会弹出这个窗口,我们选择安装位…...
React生命周期
React的生命周期主要是指React组件从创建到销毁的过程,包括三个阶段:挂载期(实例化期)、更新期(存在期)、卸载期(销毁期) 挂载期: constructor(props&#…...
SpringBoot整合RocketMQ笔记
SpringBoot版本为2.3.12.Release RocketMQ对比kafka 学习链接 https://zhuanlan.zhihu.com/p/335216381 代码实战 https://www.cnblogs.com/RedOrange/p/17401238.html Centos安装rocketmq https://blog.csdn.net/chuige2013/article/details/123783612 RocketMQ详细配置与…...
【【萌新的RiscV学习之在写代码之前对于关键路径的分析-11】】
萌新的RiscV学习之在写代码之前对于关键路径的分析-11 首先我们最简单的control 模块 全分段 因为只有分段 , 分开使用之后 , 各个阶段的具体功能才会合理使用 就像是为了后续 “气泡” 赋值 为 0 还有单独比较前递这种 EX : ALUOP ALUSrc …...
A. Sequence with Digits
题目:样例: 输入 8 1 4 487 1 487 2 487 3 487 4 487 5 487 6 487 7输出 42 487 519 528 544 564 588 628 思路: 暴力模拟题,看这数据范围,有些人可能会被唬住,以为是高精度或者容易超时,实际上…...
gitlab配置webhook限制提交注释
一、打开gitlab相关配置项 vim /etc/gitlab/gitlab.rb gitlab_shell[custom_hooks_dir] "/etc/gitlab/custom_hooks" 二、创建相关文件夹 mkdir -p /etc/gitlab/custom_hooks mkdir -p /etc/gitlab/custom_hooks/post-receive.d mkdir -p /etc/gitlab/custom_h…...
蓝桥杯Python scratch C++选拔赛stema个人如何报名?
如果不会操作,可以微信makytony协助。...
Cesium实现动态旋转四棱锥(2023.9.11)
Cesium实现动态悬浮旋转四棱锥效果 2023.9.11 1、引言2、两种实现思路介绍2.1 思路一:添加已有的四棱锥(金字塔)模型实现(简单但受限)2.2 思路二:自定义四棱锥几何模型实现(复杂且灵活ÿ…...
2023最新PS(photoshop)Win+Mac免费下载安装包及教程内置AI绘画-网盘下载
2023最新PS(photoshop)WinMac免费下载安装包及教程内置AI绘画-网盘下载 2023最新PS(photoshop)免费下载安装教程来咯~ 「PhotoShop」全套,winmac: https://pan.quark.cn/s/9d8d8ef5c400#/list/share 所有版本都有 1,复制链接…...
【JAVA】为什么要使用封装以及如何封装
个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 前言 Java的封装指的是在一个类中将数据和方法进行封装,使其可以保护起来,只能在该类内部访问,而不允许外部直接访问和修改。这是Java面向对象编程的三…...
18.示例程序(编码器接口测速)
STM32标准库开发-各章节笔记-查阅传送门_Archie_IT的博客-CSDN博客https://blog.csdn.net/m0_61712829/article/details/132434192?spm1001.2014.3001.5501 main.c #include "stm32f10x.h" // Device header #include "Delay.h" #incl…...
【超详细】Fastjson 1.2.24 命令执行漏洞复现-JNDI简单实现反弹shell(CVE-2017-18349)
前言: 看了很多别人关于漏洞复现过程,很多博客过程简洁,有的过程过于复杂,比如看到写java代码,用javac进行编译等等。所以我想写出比较详细的漏洞复现过程。 一,漏洞介绍 1-1 fastjson是什么 fastjson是…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
