list模拟实现【C++】
文章目录
- 全部的实现代码放在了文章末尾
- 准备工作
- 包含头文件
- 定义命名空间
- 类的成员变量
- 为什么节点类是用struct而不是class呢?
- 为什么要写get_head_node?
- 迭代器
- 迭代器在list类里的实例化和重命名
- 普通迭代器
- operator->()的作用是什么?
- const迭代器
- 反向迭代器
- 迭代器的获取
- 构造函数
- 默认构造
- 使用n个val构造
- 迭代器区间构造
- 解决迭代器区间构造 和 用n个val构造的冲突
- initializer_list构造
- 拷贝构造
- 析构函数
- swap
- 赋值运算符重载
- erase
- 删除pos迭代器指向的节点
- 为什么要返回next?
- 删除迭代器区间
- insert
- 在迭代器pos之前插入一个节点
- 为什么要返回newnode?
- 在迭代器pos之前插入一个迭代器区间的数据
- push_back
- push_front
- pop_front
- pop_back
- size
- empty
- back
- front
- assign
- resize
- clear
- 全部代码
全部的实现代码放在了文章末尾
准备工作
创建两个文件,一个头文件mylist.hpp
,一个源文件test.cpp
【因为模板的声明和定义不能
分处于不同的文件中,所以把成员函数的声明和定义放在了同一个文件mylist.hpp
中】
mylist.hpp:存放包含的头文件,命名空间的定义,成员函数和命名空间中的函数的定义
test.cpp:存放main函数,以及测试代码
包含头文件
-
iostream:用于输入输出
-
assert.h:用于使用报错函数assert
定义命名空间
在文件mylist.hpp
中定义上一个命名空间mylist
把list类和它的成员函数放进命名空间封装起来,防止与包含的头文件中的函数/变量重名的冲突问题
类的成员变量
参考了stl源码中的list的实现,stl中list的底层链表是双向带头循环链表
【可以看我这篇文章了解双向带头循环链表
的实现:链表的极致——带头双向循环链表】
成员变量只有一个,就是指向双向带头循环链表
的头节点的指针。
节点类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PgC6iunX-1720663070797)(https://i-blog.csdnimg.cn/direct/667727a5535e4a7c95e931618f6a1662.png#pic_center)]
为什么节点类是用struct而不是class呢?
因为节点类里面的成员变量在实现list的时候需要经常访问,所以需要节点类的成员变量是公有的【使用友元也可以,但是比较麻烦】
struct的默认访问权限就是公有,不用加访问限定符了,stl中实现的节点类也是struct
class的默认访问权限是私有的
list类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKrXfgK4-1720663070798)(https://i-blog.csdnimg.cn/direct/2546f7b1d9fe49029c3dc6aa0fd84f00.png#pic_center)]
为什么要写get_head_node?
因为插入节点之前必须
要有头节点
所以把创建初始头节点的操作写成了一个函数,用于
所有构造函数插入节点之前进行申请头节点
迭代器
因为list存储数据的方式是创建一个一个的节点存储数据
所以存储数据的空间不是
连续的,所以不能
直接用指针作为迭代器
因为指向一个节点的指针直接++,是不一定能
指向下一个节点的
所以要把迭代器实现成一个类
,这样才可以正确地支持++,- -,*等操作
迭代器在list类里的实例化和重命名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qjqNyMDi-1720663070799)(https://i-blog.csdnimg.cn/direct/506afbbbf0534c61b435d2d371e2113d.png#pic_center)]
普通迭代器
template<class T, class R, class F>
struct Iterator
{把自己的类型重命名一下typedef Iterator<T, R, F> Self;成员变量的类型是 双向带头循环链表的节点类型listnode<T>* _n;Iterator(listnode<T>*l=nullptr) 构造函数{_n = l;}Self& operator++()前置++{++就是指向下一个节点_n = _n->_next;return *this;}Self operator++(int) 后置++{Self tmp =*this; 先记录一下++之前的值_n = _n->_next; 再++return tmp;}Self& operator--() 前置--{--就是指向上一个节点_n = _n->_prev;return *this;}Self operator--(int) 后置--{Self tmp = *this; 先记录一下--之前的值_n = _n->_prev; 再--return tmp;}R operator*()const{类比指针*就是获取 节点中存储的数据return _n->_data;}F operator->()const{返回 节点中 存储数据的成员变量的 地址return &(_n->_data);}bool operator!=(const Self&obj)const{return _n != obj._n;}bool operator==(const Self& obj){return !(*this != obj);}
};
operator->()的作用是什么?
为了实现:
当节点中存储的数据是自定义类型的变量
时,可以直接使用 迭代器->访问自定义类型中的成员
【原来访问需要调用两次->,为了可读性,省略了一个->
】
例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kD0EkJ8j-1720663070800)(https://i-blog.csdnimg.cn/direct/68c293cf351f423fb9a05da5b82d6196.png#pic_center)]
const迭代器
const迭代器与普通迭代器的区别是什么?
区别就只有==不能
通过const迭代器改变节点中存储的数据==
转换一下就是:
不能
使用迭代器的operator*()
改变节点中存储的数据
,即把operator*()
的返回值改成const T&
就可以了不能
使用迭代器的operator->()
改变节点中存储的数据的成员
,即把operator->()
的返回值改成const T*
就可以了
所以const迭代器与普通迭代器的区别就只有两个函数的返回值类型不同,所以增加两个模板参数:R和F
普通迭代器
实例化时:R就是T&
,F就是T*
const迭代器
实例化:R就是const T&
,F就是const T*
反向迭代器
反向迭代器与普通迭代器的实现上
的区别就是:
普通
迭代器++是指向下一个
节点
反向
迭代器++是指向上一个
节点
普通
迭代器- -是指向上一个
节点
反向
迭代器- -是指向下一个
节点
template<class T, class R, class F>
struct Reverse_iterator
{把自己的类型重命名一下typedef Reverse_iterator<T, R, F> Self;成员变量的类型是 双向带头循环链表的节点类型listnode<T>* _n;Reverse_iterator(listnode<T>* l) 构造函数{_n = l;}Self& operator++(){反向迭代器++,是移动到 前 一个节点_n = _n->_prev;return *this;}Self operator++(int){Self tmp = *this;_n = _n->_prev;return tmp;}Self& operator--(){反向迭代器--,是移动到 后 一个节点_n = _n->_next;return *this;}Self operator--(int){Self tmp = *this;_n = _n->_next;return tmp;}R operator*()const{return _n->_data;}F operator->()const{return &(_n->_data) ;}bool operator!=(const Self& obj)const{return _n!=obj._n;}bool operator==(const Self& obj)const{return !(*this != obj);}
};
迭代器的获取
iterator begin()
{头节点的下一个节点 才是第一个节点return _head->_next;
}const修饰的对象只能调用const修饰的成员函数
const_iterator begin()const
{头节点的下一个节点 才是第一个节点return _head->_next;
}iterator end()
{最后一个节点的下一个节点是 头节点因为是循环链表return _head;
}const修饰的对象只能调用const修饰的成员函数
const_iterator end()const
{return _head;
}reverse_iterator rend()
{反向迭代器的rend返回的是第一个节点的 前一个节点return _head;
}const修饰的对象只能调用const修饰的成员函数
const_reverse_iterator rend()const
{return _head;
}
reverse_iterator rbegin()
{反向迭代器的rbegin()返回的是 最后一个节点最后一个节点是 头节点的前一个因为是循环链表return _head->_prev;
}const_reverse_iterator rbegin()const
{return _head->_prev;
}
构造函数
默认构造
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GgtecBB-1720663070800)(https://i-blog.csdnimg.cn/direct/90c95c5207f2461193a7424ceaff1e9a.png#pic_center)]
使用n个val构造
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtIPk0if-1720663070801)(https://i-blog.csdnimg.cn/direct/e72aeab61a5e4eac938b0b20fb9645f4.png#pic_center)]
迭代器区间构造
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xfp8vYpy-1720663070802)(https://i-blog.csdnimg.cn/direct/9c03aa701d1e4067bf92b0fa220e0918.png#pic_center)]
解决迭代器区间构造 和 用n个val构造的冲突
当重载了迭代器区间构造
和使用n个val构造
的时候
如果传入的两个参数都是int类型
的话就会报错
为什么?
因为在模板函数构成重载时,编译器会调用更合适的那一个
什么叫更合适?
就是不会
类型转
如果传入的两个参数都是int类型
,那么调用的应该是使用n个值构造
,因为没有int类型的迭代器
但是使用n个值构造
的第一个参数是size_t , int传进去要隐式类型转换
而调用迭代器区间构造
,两个int的实参传进去,就会直接把InputIterator
推导成int,不会
发生类型转换,所以编译器会调用迭代器区间构造
解决方法:
再重载一个使用n个值构造
的函数,把第一个参数改成int,这样根据模板偏特化
,就会在都不类型转换时优先调用第一个参数特化成int的那个构造函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYHmqtjL-1720663070802)(https://i-blog.csdnimg.cn/direct/1dd1ac43d7e7464c9d36b43f26922525.png#pic_center)]
initializer_list构造
写了这个构造函数,就可以支持直接使用{}初始化了
例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nzwhbmge-1720663070803)(https://i-blog.csdnimg.cn/direct/d80c8432dcf048999e714c96ff24d94b.png#pic_center)]
initializer_list
是iostream库里面的自定义类型,它可以直接接收{ }里面的值 进行初始化
,而且有迭代器
所以可以直接使用迭代器循环+尾插进行对list
的构造
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMz3wcLb-1720663070803)(https://i-blog.csdnimg.cn/direct/a4069ab1989e43528048b1a7e1aec98f.png#pic_center)]
拷贝构造
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-et0I5RLp-1720663070804)(https://i-blog.csdnimg.cn/direct/dbb9b340399c400f8081c14424aabfb6.png#pic_center)]
析构函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ymsiGjft-1720663070804)(https://i-blog.csdnimg.cn/direct/5d56f67c54924a40b357464baeb17539.png#pic_center)]
swap
只需要交两个list对象的头指针中存储的地址
就可以了
因为两个list对象都有头结点,交换了头指针中存储的地址
,就相当于把这两个对象的头指针的指向交换了,
而链表的所有节点都是由头节点出发去找到的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jY0TYhg6-1720663070805)(https://i-blog.csdnimg.cn/direct/f2a37ed2853348448b1ceb75e46d787d.png#pic_center)]
void swap(list& obj)
{调用库里面的swap,交换头指针std::swap(_head, obj._head);
}
赋值运算符重载
list& operator= (list obj)
{swap(obj);return *this;
}
为什么上面的两句代码就可以完成深拷贝呢?
这是因为:
使用了传值传参,会在传参之前调用拷贝构造,再把拷贝构造出的临时对象作为参数传递进去
赋值运算符的左操作数,*this再与传入的临时对象obj交换,就直接完成了拷贝
在函数结束之后,存储在栈区的obj再函数结束之后,obj生命周期结束
obj调用析构函数,把指向的从*this那里交换来的不需要的空间销毁
erase
删除pos迭代器指向的节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lEoZYbB4-1720663070805)(https://i-blog.csdnimg.cn/direct/52c9e57f560f4af8b67d46f3bd0c7f4c.png#pic_center)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3ji5vVM-1720663070808)(https://i-blog.csdnimg.cn/direct/cf348bb14b83422f9bb24da46f5d11a1.png#pic_center)]
为什么要返回next?
因为使用了erase之后的迭代器会失效,需要提供更新的方法
为什么使用了erase之后的迭代器会失效?
因为pos指向的节点erase之后,节点被释放了
stl库里面规定erase的返回值是指向删除数据的下一个数据的迭代器
,下一个数据就是next指向的数据,所以返回next【没有接收返回值的迭代器,在检测较严格的编译器中,不管指向的位置是否正确,都会禁止使用,使用了就报错
】
删除迭代器区间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQ0tx5C9-1720663070809)(https://i-blog.csdnimg.cn/direct/af38f109bbbf4a578c86b6cba3001a80.png#pic_center)]
insert
在迭代器pos之前插入一个节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SyriqhUO-1720663070810)(https://i-blog.csdnimg.cn/direct/66a804199ba14c179a1dd878db168ea8.png#pic_center)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BFOPpreV-1720663070810)(https://i-blog.csdnimg.cn/direct/600ce34d9a304e4286959dbba11c880c.png#pic_center)]
为什么要返回newnode?
list的迭代器
pos在使用完insert之后其实是不会失效
的
但是为了与其他容器的nsert的返回值进行统一,所以也返回了指向新插入的节点的迭代器
在迭代器pos之前插入一个迭代器区间的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7FlTK7F-1720663070811)(https://i-blog.csdnimg.cn/direct/d2549108a9314de4a9c74e4bdedb5ab2.png#pic_center)]
push_back
复用insert即可
void push_back(const T&val)
{insert(end(), val);
}
push_front
复用insert即可
void push_front(const T& val)
{insert(begin(), val);
}
pop_front
复用erese即可
void pop_front()
{erase(begin());
}
pop_back
复用erese即可
void pop_back()
{erase(--end());
}
size
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YbqziMa-1720663070811)(https://i-blog.csdnimg.cn/direct/0b7f0e559d16473ea5dbccc5c14e1730.png#pic_center)]
empty
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-59VtLmPm-1720663070811)(https://i-blog.csdnimg.cn/direct/e2df88fd61c74ca0b90abe613cdd9e6a.png#pic_center)]
back
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N7sLJXsd-1720663070812)(https://i-blog.csdnimg.cn/direct/3774472c4f38438db90590472b159be5.png#pic_center)]
front
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jarz2two-1720663070812)(https://i-blog.csdnimg.cn/direct/3f74f58427864bf0bac0e5a93d555454.png#pic_center)]
assign
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9gevGVoC-1720663070814)(https://i-blog.csdnimg.cn/direct/4620fd900e984ce3900311a61ab68762.png#pic_center)]
resize
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69kXAjpz-1720663070814)(https://i-blog.csdnimg.cn/direct/d199dc1abebb4b17b161006eea102d00.png#pic_center)]
clear
复用erase
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NdJzlJjt-1720663070815)(https://i-blog.csdnimg.cn/direct/154c04d0e7504bc684e9d2ab98667ae0.png#pic_center)]
全部代码
#include<iostream>
#include<assert.h>using namespace std;namespace mylist
{template<class T>struct listnode//双向带头循环链表的节点类{T _data;//节点存储的数据listnode* _next;//指向下一个节点的指针listnode* _prev;//指向前一个节点的指针};template<class T, class R, class F>struct Iterator{//把自己的类型重命名一下typedef Iterator<T, R, F> Self;//成员变量的类型是 双向带头循环链表的节点类型listnode<T>* _n;Iterator(listnode<T>*l=nullptr)//构造函数{_n = l;}Self& operator++()//前置++{//++就是指向下一个节点_n = _n->_next;return *this;}Self operator++(int)//后置++{Self tmp =*this;//先记录一下++之前的值_n = _n->_next;//再++return tmp;}Self& operator--()//前置--{//--就是指向上一个节点_n = _n->_prev;return *this;}Self operator--(int)//后置--{Self tmp = *this;//先记录一下--之前的值_n = _n->_prev;//再--return tmp;}R operator*()const{//类比指针//*就是获取 节点中存储的数据return _n->_data;}F operator->()const{//返回 节点中 存储数据的成员变量的 地址return &(_n->_data);}bool operator!=(const Self&obj)const{return _n != obj._n;}bool operator==(const Self& obj){return !(*this != obj);}};template<class T, class R, class F>struct Reverse_iterator{//把自己的类型重命名一下typedef Reverse_iterator<T, R, F> Self;//成员变量的类型是 双向带头循环链表的节点类型listnode<T>* _n;Reverse_iterator(listnode<T>* l)//构造函数{_n = l;}Self& operator++(){//反向迭代器++,是移动到 前 一个节点_n = _n->_prev;return *this;}Self operator++(int){Self tmp = *this;_n = _n->_prev;return tmp;}Self& operator--(){//反向迭代器--,是移动到 后 一个节点_n = _n->_next;return *this;}Self operator--(int){Self tmp = *this;_n = _n->_next;return tmp;}R operator*()const{return _n->_data;}F operator->()const{return &(_n->_data) ;}bool operator!=(const Self& obj)const{return _n!=obj._n;}bool operator==(const Self& obj)const{return !(*this != obj);}};template<class T>class list{//把双向带头循环链表的节点类型重命名成nodetypedef listnode<T> node;private:node* _head;//唯一的成员变量//获取初始头结点node* get_head_node(){//申请一个节点大小的空间node* tmp = new node;//最开始的头节点的prev和next都指向自己tmp->_next = tmp;tmp->_prev = tmp;return tmp;}public:typedef Iterator<T, T&, T*> iterator;//普通迭代器typedef Iterator<T, const T&, const T*> const_iterator;//const迭代器typedef Reverse_iterator<T, T&, T*> reverse_iterator;//反向迭代器typedef Reverse_iterator<T, const T&, const T*> const_reverse_iterator;//const反向迭代器list(){//获取头结点//为之后的插入操作做准备_head=get_head_node();}list(size_t n, const T& val=T()){//必须先获取头节点,才能进行插入数据_head = get_head_node();for (int i = 0; i < n; i++){push_back(val);//尾插n次}}template <class InputIterator>list(InputIterator first, InputIterator last){//必须先获取头节点,才能进行插入数据_head = get_head_node();while (first != last){//把解引用之后的值,一个一个尾插进去push_back(*first);first++;}}list(int n, const T& val = T()){//必须先获取头节点,才能进行插入数据_head = get_head_node();for (int i = 0; i < n; i++){push_back(val);}}list(initializer_list<T> il){//必须先获取头节点,才能进行插入数据_head = get_head_node();auto it = il.begin();while (it != il.end()){//把解引用之后的值,一个一个尾插进去push_back(*it);it++;}}list(const list& obj){//必须先获取头节点,才能进行插入数据_head = get_head_node();//使用const迭代器接收 const修饰的对象的迭代器const_iterator it = obj.begin();while (it != obj.end()){//把解引用之后的获得的值,一个一个尾插进去push_back(*it);it++;}}~list(){//先把list里面除了头结点//以外的节点全部删除clear();//再把头结点申请的空间释放delete _head;_head = nullptr;}list& operator= (list obj){swap(obj);return *this;}void swap(list& obj){//调用库里面的swap,交换头指针std::swap(_head, obj._head);}iterator begin(){//头节点的下一个节点 才是第一个节点return _head->_next;}//const修饰的对象只能调用const修饰的成员函数const_iterator begin()const{//头节点的下一个节点 才是第一个节点return _head->_next;}iterator end(){//最后一个节点的下一个节点是 头节点//因为是循环链表return _head;}//const修饰的对象只能调用const修饰的成员函数const_iterator end()const{return _head;}reverse_iterator rend(){//反向迭代器的rend返回的是第一个节点的 前一个节点return _head;}//const修饰的对象只能调用const修饰的成员函数const_reverse_iterator rend()const{return _head;}reverse_iterator rbegin(){//反向迭代器的rbegin()返回的是 最后一个节点//最后一个节点是 头节点的前一个//因为是循环链表return _head->_prev;}const_reverse_iterator rbegin()const{return _head->_prev;}void push_back(const T&val){/*node* tail = _head->_prev;node* newnode = new node;newnode->_data = val;newnode->_next = _head;newnode->_prev = tail;tail->_next = newnode;_head->_prev = newnode;*/insert(end(), val);}iterator erase(iterator pos){//不能 把 头节点 给删了assert(pos!=end());//记录pos的前一个节点(prev) // 和后一个节点(next)node* prev = pos._n->_prev;node* next = pos._n->_next;//让prev的下一个节点变成nextprev->_next = next;//让prev的上一个节点变成nextnext->_prev = prev;//释放pos指向的节点delete pos._n;//返回被删除的节点的下一个节点//用于更新迭代器return next;}iterator erase(iterator first, iterator last){iterator it = first;while (it != last){//删除it指向的节点//删除后让it接收返回值,进行更新it = erase(it);}//返回被删除的 最后一个节点 的下一个节点//用于更新迭代器return last;}bool empty() const{//size==0就 是空 返回true//size!=0就 不是空 返回falsereturn size() ==0 ;}size_t size()const {//用count记录节点个数size_t count = 0;//使用const迭代器接收 const修饰的对象的迭代器const_iterator it = begin();//遍历链表while (it != end()){count++;++it;}return count;}T& back(){//list不能为空,为空就报错assert(!empty());//end()返回的迭代器指向 头结点//头结点的上一个节点就是,最后一个节点//因为是循环链表return *(--end());}//const修饰的成员,只能调用const修饰的成员函数const T& back() const{assert(!empty());return *(--end());}T& front(){//list不能为空,为空就报错assert(!empty());//begin()返回的迭代器 就指向第一个节点return *begin();}//const修饰的成员,只能调用const修饰的成员函数const T& front()const{assert(!empty());return *begin();}template <class InputIterator>void assign(InputIterator first, InputIterator last){//先把数据现有的节点(除了头结点)都删除clear();//再循环把数据一个一个尾插进去while (first != last){//尾插push_back(*first);first++;}}void assign(size_t n, const T& val){clear();for (int i = 0; i < n; i++){push_back(val);}}void assign(int n, const T& val){clear();for (int i = 0; i < n; i++){push_back(val);}}iterator insert(iterator pos, const T& val){//记录指向 pos 前一个节点的指针node* prev = pos._n->_prev;//申请新节点的空间node* newnode = new node;//存储数据newnode->_data = val;newnode->_next = pos._n;newnode->_prev = prev;prev->_next = newnode;pos._n->_prev = newnode;//返回指向新插入的节点的迭代器//用于更新迭代器return newnode;}template <class InputIterator>void insert(iterator pos, InputIterator first, InputIterator last){while (first!=last){//循环插入即可//因为list的迭代器使用完insert之后 不会失效//所以不用接收返回值 也可以insert(pos,*first);first++;}}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}void pop_back(){erase(--end());}void resize(size_t n, const T& val = T()){//获取一下size,加快后续比较效率//因为获取size()的时间复杂度为 O(N)size_t size = this->size();if (n > size){//缺失的数据用val填上//填到size()==n为止while (size < n){push_back(val);++size;}}else {//把多出来的数据(节点)删除//删除到n==size()为止while (n < size){pop_back();n++;}}}void clear(){//[begin(),end())之间的数据//就是所有的有效数据(节点)//复用erase删除即可erase(begin(), end());}};
}
相关文章:
list模拟实现【C++】
文章目录 全部的实现代码放在了文章末尾准备工作包含头文件定义命名空间类的成员变量为什么节点类是用struct而不是class呢?为什么要写get_head_node? 迭代器迭代器在list类里的实例化和重命名普通迭代器operator->()的作用是什么? const迭代器反向迭…...
nginx正向代理、反向代理、负载均衡
nginx.conf nginx首要处理静态页面 反向代理 动态请求 全局模块 work processes 1; 设置成服务器内核数的两倍(一般不不超过8个超过8个反而会降低性能一般4个 1-2个也可以) netstat -antp | grep 80 查端口号 *1、events块:* 配置影响ngi…...
matlab 有倾斜的椭圆函数图像绘制
matlab 有倾斜的椭圆函数图像绘制 有倾斜的椭圆函数图像绘制xy交叉项引入斜线负向斜线成分正向斜线成分 x^2 y^2 xy 1 (负向)绘制结果 x^2 y^2 - xy 1 (正向)绘制结果 有倾斜的椭圆函数图像绘制 为了确定椭圆的长轴和短轴的…...
PTK是如何加密WLAN单播数据帧的?
1. References WLAN 4-Way Handshake如何生成PTK?-CSDN博客 2. 概述 在Wi-Fi网络中,单播、组播和广播帧的加密算法是由AP决定的。其中单播帧的加密使用PTK密钥,其PTK的密钥结构如下图所示: PTK的组成如上图所示,由K…...
Django之登录权限系统
本文参考链接django之auth模块(用户认证) - chchcharlie、 - 博客园 (cnblogs.com) 执行完迁移命令,会自动生成admin表,迁移命令如下: python manage.py makemigrations python manage.py migrate 相关模块 from django.contrib …...
rust way step 1
install rust CARGO_HOME D:\rust\.cargo RUSTUP_HOME D:\rust\.rustup [dependencies] ferris-says "0.2" vscode 安装rust 插件 use ferris_says::say; // from the previous step use std::io::{stdout, BufWriter};fn main() {let stdout stdout();let m…...
视觉语言模型导论:这篇论文能成为你进军VLM的第一步
近些年,语言建模领域进展非凡。Llama 或 ChatGPT 等许多大型语言模型(LLM)有能力解决多种不同的任务,它们也正在成为越来越常用的工具。 这些模型之前基本都局限于文本输入,但现在也正在具备处理视觉输入的能力。如果…...
Postman工具基本使用
一、安装及基本使用 安装及基本使用参见外网文档:全网最全的 postman 工具使用教程_postman使用-CSDN博客 建议版本:11以下,比如10.x.x版本。11版本以后貌似是必须登录使用 二、禁止更新 彻底禁止postman更新 - 简书 host增加࿱…...
uni-app三部曲之三: 路由拦截
1.引言 路由拦截,个人理解就是在页面跳转的时候,增加一级拦截器,实现一些自定义的功能,其中最重要的就是判断跳转的页面是否需要登录后查看,如果需要登录后查看且此时系统并未登录,就需要跳转到登录页&…...
专注于国产FPGA芯片研发的异格技术Pre-A+轮融资,博将控股再次投资
近日,苏州异格技术有限公司(以下简称“异格技术”)宣布成功完成数亿元的Pre-A轮融资,由博将控股在参与Pre-A轮投资后,持续投资。这标志着继2022年获得经纬中国、红点中国、红杉中国等机构数亿元天使轮融资后࿰…...
【python】QWidget父子关系,控件显示优先级原理剖析与应用实战演练
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
CTF php RCE(三)
0x07 日志文件包含 判断类型 使用kali curl -I urlF12 打开F12开发者工具,选中之后F5刷新查看server类型即可 配置文件 直接包含或者访问如果有回显就是, NGINX:NGINX 的配置文件通常位于 /etc/nginx/ 目录下,具体的网站配…...
Android 注解的语法原理和使用方法
Android 注解的语法原理和使用方法 关于我 在 Android 开发中,注解(Annotation)是一种强大的工具,用于在代码中添加元数据。注解可以简化代码、提高可读性、减少样板代码,并且在一定程度上增强编译时的类型检查。本文…...
YOLOv10改进 | Conv篇 | 利用FasterBlock二次创新C2f提出一种全新的结构(全网独家首发,参数量下降70W)
一、本文介绍 本文给大家带来的改进机制是利用FasterNet的FasterBlock改进特征提取网络,将其用来改进ResNet网络,其旨在提高计算速度而不牺牲准确性,特别是在视觉任务中。它通过一种称为部分卷积(PConv)的新技术来减少…...
实验-ENSP实现防火墙区域策略与用户管理
目录 实验拓扑 自己搭建拓扑 实验要求 实验步骤 整通总公司内网 sw3配置vlan 防火墙配置IP 配置安全策略(DMZ区内的服务器,办公区仅能在办公时间内(9: 00- 18:00)可以访问,生产区的设备全天可以访问) 配置nat策…...
【游戏客户端】大话slg玩法架构(二)背景地图
【游戏客户端】大话slg玩法架构(二)背景地图 大家好,我是Lampard家杰~~ 今天我们继续给大家分享SLG玩法的实现架构,关于SLG玩法的介绍可以参考这篇上一篇文章:【游戏客户端】制作率土之滨Like玩法 PS:和之前…...
git-工作场景
1. 远程分支为准 强制切换到远程分支并忽略本地未提交的修改 git fetch origin # 获取最新的远程分支信息 git reset --hard origin/feature_server_env_debug_20240604 # 强制切换到远程分支,并忽略本地修改 2. 切换分支 1. **查看所有分支:**…...
coco dataset标签数据结构(json文件)
COCO数据集现在有3种标注类型:object instances(目标实例), object keypoints(目标上的关键点), 和image captions(看图说话),使用json文件存储。 NameImagesLabelstrain linkhttp:…...
GaussDB关键技术原理:高性能(四)
GaussDB关键技术原理:高性能(三)从查询重写RBO、物理优化CBO、分布式优化器、布式执行框架、轻量全局事务管理GTM-lite等五方面对高性能关键技术进行了解读,本篇将从USTORE存储引擎、计划缓存计划技术、数据分区与分区剪枝、列式存…...
总结之企业微信(一)——创建外部群二维码,用户扫码入群
创建外部群 企微接口中没有直接通过服务端API接口创建外部群 可以通过jssdk创建外部群:引用jssdk调用会话接口wx.openEnterpriseChat https://work.weixin.qq.com/api/doc/90000/90136/90511 创建外部群二维码 需要通过企业微信的应用,并且配置客户联…...
透视数据治理:企业如何衡量数据治理的效果?
在企业运营中,各个业务部门的成功与否都是直观且易于量化的,像销售部门卖了多少产品又为企业带来多少盈利,这些都能用具体的数字来说话。但当谈到数据治理的成效时,许多企业与决策者却感到迷茫。 数据治理的重要性不言而喻&#…...
ERC20查询操作--获取ERC20 Token的余额
获取ERC20 Token的余额 https://blog.csdn.net/wypeng2010/article/details/81362562 通过REST查询 curl -X POST --data-binary {"jsonrpc":"2.0","method":"eth_call","params":[{"from": "0x954d1a58c7a…...
Linux运维:MySQL中间件代理服务器,mycat读写分离应用实验
Mycat适用的场景很丰富,以下是几个典型的应用场景: 1.单纯的读写分离,此时配置最为简单,支持读写分离,主从切换 2.分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片 3.多租户应…...
css文字自适应宽度动态出现省略号...
前言 在列表排行榜中通常会出现的一个需求:从左到右依次是名次、头像、昵称、徽标、分数。徽标可能会有多个或者没有徽标,徽标长度是动态的,昵称如果过长要随着有无徽标进行动态截断出现省略号。如下图布局所示(花里胡哨的底色是…...
边缘计算盒子_B100_Jetson Nano (aarch64)开发环境搭建
目录 一、刷机步骤1、搭建刷机环境2、进入刷机模式3、开始刷机 二、系统迁移到TF卡 或者 U盘1、迁移脚本2、提前插入U盘或者TF卡3、 开始迁移 三、搭建miniconda 环境1、下载安装 四、jetpack开发套件环境1、版本查看2、apt 更换国内源3、安装Jetson-stats管理工具 一、刷机步骤…...
【Superset】dashboard 自定义URL
URL设置 在发布仪表盘(dashboard)后,可以通过修改看板属性中的SLUG等,生成url 举例: http://localhost:8090/superset/dashboard/test/ 参数设置 以下 URL 参数可用于修改仪表板的呈现方式:此处参考了官…...
【Linux网络】IP协议{初识/报头/分片/网段划分/子网掩码/私网公网IP/认识网络世界/路由表}
文章目录 1.入门了解2.认识报头3.认识网段4.路由跳转相关指令路由 该文诸多理解参考文章:好文! 1.入门了解 用户需求:将我的数据可靠的跨网络从A主机送到B主机 传输层TCP:由各种方法(流量控制/超时重传/滑动窗口/拥塞…...
香蕉派BPI-Wifi6迷你路由器公开发售
Banana Pi BPI-Wifi6 Mini 公开发售。 Banana Pi BPI-Wifi6 Mini 开源路由器采用Triductor TR6560 TR5220 wifi SOC设计,是一款迷你尺寸的wifi6路由器解决方案。内置高性能双核ARM Cortec A9处理器用于WIFI报文转发或智能业务处理,内置高性能LSW和硬件N…...
WPF-控件样式设置
1、控件样式设置 1.1、内嵌式为相同控件设置样式 <Window.Resources><Style TargetType"Button"><Setter Property"Background" Value"Yellow"></Setter><Setter Property"Width" Value"60"&g…...
C++20中的指定初始化器(designated initializers)
指定初始化器(designated initializers, 指定初始值设定项)语法如下:C风格指定初始化器语法,初始化数据成员的一种便捷方式 T object { .des1 arg1, .des2 { arg2 } ... }; T object { .des1 arg1, .des2 { arg2 } ... }; 说明: 1.每个指…...
做dj音乐网站/网站系统
2021 年了,前端技术日新月异,发展迅速,前端公众号是不是感觉越来越多了?在着辞旧迎新之际,这里盘点几个前端开发工程师 2021 年必须关注的优质公众号,希望对你有所帮助。大家可以像我一样,利用碎…...
电商网站怎么做支付/成人电脑速成培训班
你好,我感觉你这写错了,首先第一步要对close做差分,然后通过这个差分与0的大小关系来进一步计算,我感觉你代码里没有这个判断(我是门外汉,我也不清楚,希望可以帮到你)def rsi(price,period6):import pandas…...
网站 空间转移/网站收录查询代码
1990年,一年期的存款基准利率是10.08% ,现在一年期的存款基准利率是1.5% 你觉得这个利率低,其实其他国家更低。目前,英国央行基准利率为0.75%,瑞士央行基准利率为-0.75%,日本央行基准利率只有-0.1%。 从这些…...
报名网站建设公司哪里有/推广免费
今天在网上发现这样一个不错的小软件,通过这个软件在Windows XP机器上调试多个Web程序就方便了。软件名称:IIS admin下载地址:http://www.firstserved.net/services/iisadmin.php使用方法:运行IIS admin, 会在右下角出现小图标&am…...
自己怎么开发app/seo联盟
10月26日,“游侠汇”在上虞e游小镇成功举行。作为一场为年轻的数字文化创客们准备的一场集运动、电竞、音乐、潮流于一身的盛大嘉年华,游侠汇现场气氛热烈非凡,5000余位来自全国各地的年轻人齐聚,100余家入驻企业参与活动。①次元…...
专业做家具的网站/如何做网站优化seo
Word是我们常用的的办公软件,广泛被运用,那么我们怎么把Word转换为网页html格式? 文件:url80.ctfile.com/f/25127180-734987515-e5f15e?p551685 (访问密码: 551685) 需要软件: word2003 或 wps 个人建议用wps更方便…...