【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是…...
【牛客网】JZ39 数组中出现次数超过一半的数字
题目 思路 思路1 将数组排序,再保证有结果的情况下,此时数组中间的数字就是想要的结果 思路2 在保证有结果的情况下,此时数组的的众数是数组长度的一半以上 所以我们可以通过抵消的做法来找到最终的结果 我们可以从头遍历这个数组,如果两个数不相同,则消去这两个数,最坏的…...
【Mysql】Lock wait timeout exceeded; try restarting transaction
出现这种问题通常是有事务长时间未提交导致的 可以使用以下sql 查询事务进程 然后通过 kill 线程ID 的方式 ,结束该事务 SELECTtrx_id AS 事务ID,trx_mysql_thread_id AS 线程ID,trx_state AS 事务状态,trx_started AS 开始时间,trx_tables_locked AS 锁定的表,trx_query AS …...
python生成中金所期权行权价
参考沪深300股指期权的合约表,写一个工具函数: 使用方法 def get_format_option_gap(value: float, deviation: int 0): # 根据中证1000指数获取点位"""根据标准的行权价,生成不同档位的期权列表,适合中金所:…...
CentOS7.9 安装postgresql
# 添加postgres账户 sudo groupadd postgres sudo useradd -g postgres postgres # 修改postgres账号密码 passwd postgres # 安装postgresql cd ~tar zxvf postgresql-15.3.tar.gz cd postgresql-15.3./configure --prefix/usr/local/pgsql --without-readlinemake -j4 …...
qt线程介绍
目录 介绍 线程类 QThread 方式1 方式2 案例 线程资源释放 介绍 qt为多线程提供了完美的支持,实现多线程一般是从从QTHread中继承定义自己的线程类,QT也提供了QMutexLocker,QwaitCondition等类实现线程同步,与Linux系统或C中的线程库类似…...
记一次用dataframe进行数据清理
总结一下dataframe读取数据库,以及整理数据的过程。分为三个部分:数据读取,数据整理以及数据写入。 1、数据读取 从csv读取读取数据,使用pandas读的read_csv函数,传入两个参数,分别是path文件路径&#x…...
《Jetpack Compose从入门到实战》 第二章 了解常用UI组件
目录 常用的基础组件文字组件图片组件按钮组件选择器组件对话框组件进度条组件 常用的布局组件布局Scaffold脚手架 列表 书附代码 Google的图标库 常用的基础组件 文字组件 Composable fun TestText() {Column(modifier Modifier.verticalScroll(state rememberScrollState…...
Vue3 引入使用 vant组件详解
目录 Vue3 引入使用 vant组件详解1.安装2.引入2.1 全局引入2.2 按需引入2.2.1 vite项目:vite.config.js2.2.2 Webpack项目:webpack.config.js2.2.3 配置在vue.config.js中 3.使用 Vue3 引入使用 vant组件详解 Vant是一个强大的移动端组件库,目前Vant 官…...
NOSQL Redis Ubuntu系列 常用的配置 及密码登录
查看Ubuntu 版本 uname -a 配置redis.conf 查看redis 是否安装成功 ps -ef | grep redis 查看redis 服务状态 service redis status 查看redis 默认安装的路径 whereis redis #sudo vim /etc/redis.conf redis 密码登录...
C语言解析GPS源数据
文章目录 一、GPS数据格式介绍二、GPS字段含义三、C语言解析数据代码3.1 解析每个字段数据3.2 解析定位数据 一、GPS数据格式介绍 GPS(全球定位系统)数据格式常见的是NMEA 0183格式,NMEA 0183格式是一种用于导航设备间传输数据的标准格式&am…...
网站做影集安全吗/电商怎么做
Nginx模块fastcgi_cache的几个注意点 去年年底,我对nginx的fastcgi_cache进行摸索使用。在我的测试过程中,发现一些wiki以及网络上没被提到的注意点,这里分享一下。 在web项目中,大家都已经非常熟悉其架构流程了。都说Cache是万金…...
做网站建设的价格/沈阳seo排名优化教程
我们知道,硬件设备是影响电脑运行速度的一个主要因素,在强大的win7系统之中,咱们也可以通过对硬件的一些调整来实现电脑流畅度的提升,例如咱们win7电脑中就有一个硬件加速的选项按钮,只需要点击一下,就能提…...
台州网站建设/百度统计官网
写在前面还有一个月就美赛了,本系列文章适用于完全没有任何 matlab 基础,但是有别的编程语言基础的人看,我会结合自己的理解,有的放矢的讲,不会掺杂很多废话,各位读者轻喷~matlab 运算符Matlab 运算符分五类…...
邳州做网站pzwode/百度指数的功能
2019独角兽企业重金招聘Python工程师标准>>> ** 什么是ThinkSNS** ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS。 亲爱的粉丝,授…...
西安网站建设技术/百度上看了不健康的内容犯法吗
重复的随机数废话不多说,首先我们来看使用seed的一个很神奇的现象。func main() {for i : 0; i < 5; i {rand.Seed(time.Now().Unix())fmt.Println(rand.Intn(100))} }// 结果如下 // 90 // 90 // 90 // 90 // 90可能不熟悉seed用法的看到这里会很疑惑࿰…...
施工企业施工生产计划/网页关键词排名优化
数码管 多位数码管,即是两个或两个以上单个数码管并列集中在一起形成一体的数码管。当多位一体时,它们内部的公共端是独立的,而负责显示什么数字的段线全部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮&…...