【C++STL】list的模拟实现
✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
🔥 所属专栏:C++深入学习笔记
💫 欢迎来到我的学习笔记!
一、三个类与成员函数接口
在list.h
文件中实现三个类的框架以及各种成员函数接口。
结点类:
// 模拟实现list当中的结点类(一个个的结点)(结点封装成类)// 一个节点存储的信息有:数据、前驱指针、后继指针,这就是该结点类的成员变量template<class T>struct _list_node{// 成员函数_list_node(const T& val = T());// 构造函数// 成员变量:T _val; // 数据域_list_node<T>* _next; // 后继指针_list_node<T>* _prev; // 前驱指针};
迭代器类:
// 模拟实现list的迭代器(迭代器封装成类)
template<class T,class Ref,class Ptr>
struct _list_iterator
{typedef _list_node<T> node;// node:结点类的类型名称typedef _list_iterator<T, Ref, Ptr> self;// self:迭代器类的类型名称_list_iterator(node* pnode);// _list_iterator构造函数// 各种运算符重载函数self operator++();// 返回值是self,返回值是迭代器类型self operator++(int);self operator--();self operator--(int);bool operator == (const slef & s) const;bool operator!=(const self& s) const;Ref operator*();Ptr operator->();// 成员变量:node* _pnode;// 一个指向结点的指针
};
list链表类:
template<class T>class list // 链表封装成类{public:typedef _list_node<T> node; // node:结点类的类型名称typedef _list_iterator<T, T&, T*> iterator; // 普通迭代器的类型名称typedef _list_iterator<T, const T&, const T*> const_iterator;// const对象的迭代器// 默认成员函数list();list(const list<T>& lt);// list<T> operator=(const list<T> lt);// 赋值运算符的重载~list();// 析构函数:完成结点的释放// 迭代器相关函数iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;// 访问容器相关函数T& front();T& back();const T& front() const;const T& back() const;// 插入删除函数void insert(iterator pos, const T& x);iterator erase(iteartor pos);void push_back(const T& x);// 插入数据(尾插)void clear();void empty() const;void swap(list<T>& lt);// 其他函数size_t size() const;void resize(size_t n, const T& val = T());void clear();bool empty() const;void swap(list<T>& lt);private:node* _head;// 指向链表头节点的指针};
二、结点类的模拟实现
list的底层是一个带头双向循环链表,因此我们如果想要实现list,首先需要实现一个结点类,完成一个个的结点的创建。结点的信息包括:数据(_val
)、前驱指针(_prev
)、后继指针(_next
)。
对于该结点类的成员函数来说,我们只需要实现一个构造函数即可。因为该结点类只需要根据数据来构造一个结点,而结点的释放则由list的析构函数来实现。
class
与struct
关键字选择:如果不想用访问限定符来限制成员的访问,所有的成员都会用struct
。struct
默认是公有protected
,class
默认是私有private
。这里选择使用struct
:list
中会频繁地访问成员变量,想要成员变量都是公有的,就可以使用struct,但是如果使用class的话,就会用到很多的友元friend
。
惯例上,如果不想让访问限定符限制成员的访问,所有的成员都设置为公有时,就使用struct
;如果要大量访问成员变量时,就使用struct
。
结点类的构造函数:根据所给数据构造出一个结点,该结点的数据域存储的是所给的数据,前驱指针与后继指针初始化为空指针。
// 结点类的模拟实现
template<class T>
struct _list_node
{_list_node(const T& val = T())// 结点类的构造函数:_val(val),_prev(nullptr),_next(nullptr){}// 若构造结点时未传入数据,则默认以list容器所存储的默认构造函数所构造出来的值为传入数据
};
三 、迭代器类的模拟实现
3.1.迭代器类的由来
之前string
、vector
实现时都没有实现过迭代器类,为什么这里要实现一个迭代器类呢?
原因:string
、vector
对象都将他们的数据存储在了一块连续的内存空间,我们通过自增、自减以及解引用操作就可以对相应位置的数据进行一系列操作。因此string
和vector
当中的迭代器就是原生指针。
template<class T>
class vector
{
public:typedef T* iterator;// vector的迭代器就是原生指针
};
但是list
容器中的各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅仅通过结点指针的自增自减解引用等操作对相应的结点的数据进行操作。而迭代器的意义就是:让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行操作。
既然list
的结点指针的行为不满足迭代器的定义,我们可以对这个结点的指针进行封装,然后对结点指针的各种运算符操作进行重载,让我们可以用和string
和vector
中的迭代器一样的方式使用list
当中的迭代器。比如:使用list
当中的迭代器进行++
操作,实际上就执行了p = p->next;
,这种功能可以利用运算符重载来实现。
总结:list
的迭代器类实际上就是对结点指针进行了封装,对各种运算符进行了重载,使得结点指针的各种行为看起来与普通迭代器一样。
3.2.模板参数说明、
这里的迭代器类使用的模板参数有三个。
template<class T, class Ref, class Ptr>
在list
的模拟实现里,我们有两种类型的迭代器,const
迭代器和普通迭代器。
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
根据typedef
操作中,我们可以知道,T
、Ref
、Ptr
分别代表的是类型、引用类型和指针类型。
当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。若该迭代器不传入这三个模板参数,就不能很好的区分普通迭代器和const迭代器。
3.3.构造函数
迭代器类的构造函数作用:对结点的指针进行封装,成员变量只有一个——结点指针。其构造函数直接根据所给结点指针 构造一个迭代器对象即可。
_list_iterator(node* pnode)// _list_iterator构造函数:_pnode(pnode)
{}
3.4.运算符重载
3.4.1.前置++
前置++
:先让数据自增,然后返回自增后的数据。在这里的目的是:让结点指针“自增”即让结点指向下一个结点(后一个结点),然后返回自增后的结点指针。
// 前置++运算符:
self operator++() // 返回值是self,返回值是迭代器类型
{_pnode = _pnode->_next;// 指针指向下一个结点return *this;// 返回++之后的结点指针
}
3.4.2.后置++
后置++
:先记录当前结点指针的指向,然后再让结点指针指向下一个结点(“自增”),然后返回“自增”前的结点指针。
// 后置++运算符
self operator++(int)
{node* tmp = *this->_pnode;_pnode = _pnode->_next;return tmp;
}
说明:self
是当前迭代器对象的类型。
typedef _list_iterator<T, ref, Ptr> self;
3.4.3.前置–
前置--
:先让结点指针指向前一个结点(“自减”),然后返回“自减”后的结点指针。
// 前置--运算符
self operator--()
{_pnode = _pnode->_prev;return *this;
}
3.4.4.后置–
后置--
:先记录当前结点指针的指向,然后再让结点指针指向前一个结点(“自减”),再返回“自减”前的结点指针。
// 后置--运算符
self operator--(int)
{node* tmp = *this->_pnode;_pnode = _pnode->_prev;return tmp;
}
3.4.5. ==
==
运算符:使用==
运算符比较两个迭代器时,我们实际上是想知道这两个迭代器是否是同一个位置上的迭代器,也就是判断两个迭代器中的指针的指向是否相同。
bool operator == (const self& s) const
{return s._pnode == _pnode;// 判断两个结点指针是否相同
}
3.4.6. !=
!=
运算符:和==
运算符的作用相反,这里!=
是判断两个两个迭代器的指针是否不同。
bool operator!=(const self& s) const
{return s._pnode != _pnode;
}
3.4.7. *
*
运算符:在list
的模拟实现中,*
操作符的作用就是返回当前结点指针所指向的结点的数据,这里解引用后可能对数据就进行修改,因此使用引用返回,返回值类型Ref
。
Ref operator*()
{return _pnode->_val;
}
3.4.8. ->
使用到->
运算符的场景:当list容器中的每个结点存储的不是内置类型,而是自定义类型如日期类时,那么我么如果得到某个位置的迭代器,我们可能会使用->
运算符访问Date的成员。
list<Date> lt;Date d1(2024, 10, 23);Date d2(2000, 1, 1);Date d3(1949, 9, 1);lt.push_back(d1);lt.push_back(d2);lt.push_back(d3);list<Date>::iterator pos = lt.begin();cout << pos->_year << endl; //输出第一个日期的年份
使用pos->_year
这样的访问方式时,需要将日期类的成员变量设置为公有。因此->
运算符的重载:直接返回结点当中所存储数据的地址。返回值类型Ptr
。
Ptr operator->()
{return &_pnode->_val;// 返回结点当中所存储数据的地址
}
注意:这里本来应该是两个->
的:原本的调用形式是pos->operator->_year
,第一个->
是指pos调用重载的operator->
然后返回Date*
的指针,第二个->
是Date*
的指针去访问对象中的成员变量_year
。
一个地方连续两个->
,影响程序的可读性,所以编译器做了特殊识别处理,省略一个->
来增加程序的可读性。
四、list链表类的模拟实现
4.1.默认成员函数
4.1.1.构造函数
list是一个带头双向循环链表,在构造一个list对象时,直接申请了一个头结点,并让前驱指针和后继指针都指向自己。
代码:
// 无参构造函数
list()
{_head = new node;// 申请一个头结点_head->_next = _head;_head->_prev = _head;
}
4.1.2.拷贝构造函数
根据所给的list容器拷贝构造出一个对象。先申请一个头结点,让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据通过遍历的方式一个个地尾插到新构造的容器里面。
// 拷贝构造函数
list(const list<T>& lt)
{_head = new node;_head->_next = _head;_head->_prev = _head;for (auto e : lt){push_back(e);// 将容器lt当中的数据一个个尾插到新构造的容器里面}
}
4.1.3.赋值运算符的重载
- 传统写法:先调用clear函数将原容器清空,然后将容器lt当中的数据,通过遍历的方式一个个尾插到清空厚的容器当中。
// 赋值运算符的重载的传统写法:
list<T> operator=(const list<T> lt)
{if (this != <)// 避免自己给自己赋值{clear();// 清空容器for (const auto& e : lt){push_back(e);// 将容器中的数据一个个尾插到链表后面}}return *this;
}
- 现代写法:首先利用编译器机制,故意不是用引用接收参数,通过编译器哦自动调用list拷贝构造函数构造出一个list对象,然后调用swap函数将原容器与该list对象进行交换。
list<T> operator=(const list<T> lt)// 不是用引用接收参数//编译器会自动调用拷贝构造函数构造出一个list对象,然后调用swap函数将原容器与该list对象进行交换
{swap(lt);// 交换这两个对象return *this;
}
4.1.4.析构函数
先调用clear函数来清理容器中的数据,然后将头结点释放,最后将头指针置空。
// 析构函数:完成结点的释放
~list()
{clear();delete[] _head;_head = nullptr;
}
4.2.迭代器相关函数
4.2.1.begin和end
begin函数返回第一个有效数据的迭代器,end函数返回最后一个有效数据的笑一个位置的迭代器(即头节点的迭代器)。
对于list这个带头双向循环链表来说,第一个有效数据的迭代器就是使用头结点后一个结点的地址构造出来的迭代器,而其最后一个有效数据的下一个迭代器就是使用头节点的地址构造出来的迭代器(最后一个结点的下一个结点就是头结点)。
// 迭代器相关函数
iterator begin()
{return iterator(_head->_next);// 返回使用头结点的下一个结点的地址构造出来的普通迭代器
}
iterator end()
{return iterator(_head);// 返回使用头节点构造出来的迭代器
}
const对象的begin函数和end函数。
const_iterator begin() const//
{return iterator(_head->_next);// 返回使用头结点的下一个地址构造出来的const迭代器
}
const_iterator end() const
{return iterator(_head);// 返回使用头结点的地址构造出来的const迭代器
}
4.3.容器访问的相关函数
4.3.1.front和back
front函数和back函数分别用于获取第一个有效数据和最后一个有效数据。因此,实现时就返回第一个有效数据的引用和最后一个有效数据的引用。
// front函数:获取第一个有效数据——直接返回第一个有效数据的引用
T& front()
{return *begin();// begin迭代器是第一个有效数据的指针
}
// back函数:获取最后一个有效数据——返回最后一个有效数据的引用
T& back()
{return *(--end());// end迭代器是头节点的指针
}
前面是对普通对象的访问,下面是const对象的访问。const对象调用front和back函数后所得到的数据不能被修改。
const T& front() const
{return *begin();// 返回第一个有效数据的const引用
}
const T& back() const
{return *(--end());// 返回最后一个有效数据的const引用
}
4.4.插入删除函数
4.4.1.insert
insert函数:在所给迭代器之前插入一个新的结点。
根据上图所示:先判断所给迭代器pos是否合法,然后创建相关指针——根据pos迭代器创建出结点指针cur、cur指针的前一个位置的结点指针prev,然后根据所给数据x构造一个带插入新结点,最后再建立新结点与cur、prev之间的双向关系。
// 插入:在所给迭代器pos之前插入一个新的结点
void insert(iterator pos, const T& x)
{assert(pos._pnode);// 创建相关的指针node* cur = pos._pnode;// 创建cur指针node* prev = cur->_prev;// 创建prev指针node* newnode = new node(x);// 用传入的数据x创建新结点// 建立newnode与cur之间的双向关系newnode->_next = cur;cur->_prev = newnode;// 建立newnode与prev之间的双向关系newnode->_prev = prev;prev->_next = newnode;
}
4.4.2.erase
erase函数:删除所给迭代器位置的结点。
先检测pos迭代器的合法性,然后创建相关指针——根据pos迭代器创建出结点指针cur、cur指针的前一个位置的结点指针prev、cur指针后一个位置的结点指针next;释放cur指针、建立prev和next之间当然双向关系。
// 插入:在所给迭代器pos之前插入一个新的结点
void insert(iterator pos, const T& x)
{assert(pos._pnode);// 创建相关的指针node* cur = pos._pnode;// 创建cur指针node* prev = cur->_prev;// 创建prev指针node* newnode = new node(x);// 用传入的数据x创建新结点// 建立newnode与cur之间的双向关系newnode->_next = cur;cur->_prev = newnode;// 建立newnode与prev之间的双向关系newnode->_prev = prev;prev->_next = newnode;
}
4.4.3.push_back和pop_back
push_back在头结点前插入数据,pop_back删除头结点的前一个结点(即最后一个结点)。
// push_back:尾插void push_back(const T& x){insert(end(), x);// 在头结点前面插入数据(头结点的前一个位置就是尾结点)}// pop_back:尾删void pop_back(const T& x){erase(--end());}
4.4.4.push_front和pop_front
push_front函数是在第一个有效结点前插入结点(头插),pop_front是删除第一个有效结点(尾插)。
// push_front:头插
void push_front(const T& x)
{insert(begin(), x);// 在第一个有效结点前插入一个结点
}// pop_front:头删
void pop_front()
{erase(begin());// 删除第一个有效结点
}
4.5.其他函数
4.5.1.size
size:获取当前容器中的有效数据个数,list链表中只能通过遍历的方式来逐个统计有效数据个数,不能直接通过size()
函数获取。
// size:获取当前容器中的有效数据个数,list链表中只能通过遍历的方式逐个统计有效数据个数
size_t size() const
{size_t sz = 0;// 统计const_iterator it = begin();// 获取第一个有效数据的const_iteratorwhile (it != end())// 通过遍历统计有效数据个数{sz++;it++;}return sz;// 返回有效数据个数
}
4.5.2.clear
clear
函数:通过遍历的方式清空容器,逐个删除结点,只保留头结点。
// clear:通过遍历的方式清空容器,逐个删除结点,只保留头结点
void clear()
{iterator it = begin();// 从第一个迭代器开始while (it != end())// 逐个删除结点,只保留头结点{it = erase(it);// 不用++操作,因为erase间接++了}
}
4.5.3.resize
resize
函数:
- 若当前容器的size晓瑜所给的n,则尾插结点知道size为n。
- 若当前容器的size大于所给n,则只保留前n个有效数据。
实现resize
的方法是:设置一个变量len,用来记录当前所遍历的数据个数,然后开始遍历容器。在遍历的过程中如果:
- len大于或者等于n时遍历结束,释放掉该结点厚的所有结点。
- 容器遍历完成同时遍历结束,此时说明容器中的有效数据个数小于n,需要尾插结点,直到容器当中的有效数据个数为n时停止尾插。
// resize
void resize(size_t n, const T& val = T())
{iterator i = begin(); //获取第一个有效数据的迭代器size_t len = 0; //记录当前所遍历的数据个数while (len < n && i != end()){len++;i++;}if (len == n) //说明容器当中的有效数据个数大于或是等于n{while (i != end()) //只保留前n个有效数据{i = erase(i); //每次删除后接收下一个数据的迭代器}}else //说明容器当中的有效数据个数小于n{while (len < n) //尾插数据为val的结点,直到容器当中的有效数据个数为n{push_back(val);len++;}}
}
4.5.4.empty
empty
函数:判断容器是否为空,可直接判断改容其的begin函数和end函数所返回的迭代器是否为同一个位置上的迭代器。
// 判断容器是否为空
bool empty() const
{return begin() == end();
}
4.5.5.swap
swap
函数:交换两个容器,list容器中只存储了链表的头指针,所以交换两个容器的头指针即可。
在此处调用库中的swap
函数时需要在swap
之前加上::
域作用限定符,告诉编译器这里优先在全局范围寻找swap
函数,否则编译器会认为调用的是正在实现的swap
函数(即就近原则)。
// swap:交换两个容器,list容器中只存储了链表的头指针,交换两个容器的头指针即可
void swap(list<T>& lt)
{::swap(_head, lt._head);
}
push_back:尾插一个节点。找尾:_head->_prev
void push_back(const T& x)// 插入数据x,类型是T,不允许修改,使用传引用传值
{// 插入数据Node* newnode = new Node(x);// 创建了一个结点Node tail = _head->_prev;// tail是最后就一个结点(尾插新节点之前),就是head的prev前驱指针// 连接四步骤:tail->_next = newnode;// 尾结点与新结点相连newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;++_size;
}
相关文章:
【C++STL】list的模拟实现
✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛) 🌈 个人Motto:他强任他强,清风拂山冈! 🔥 所属专栏:C深入学习笔记 💫 欢迎来到我的学习笔记! 一、三个类与成员函数接口 在list.…...
以30个面试问题和案例为导向:全面解析 Java Servlet是什么?基本概念、实现原理、生命周期、类结构、请求与响应的处理机制,以及性能优化和安全性管理
Servlet 是 Java Web 开发的核心组件之一,负责处理客户端请求并生成动态响应。本文将深入探讨 Servlet 的基本概念、实现原理、生命周期、类结构、请求与响应的处理机制,以及性能优化和安全性管理,帮助开发者从多方面掌握 Servlet。 文章目录…...
MFC小游戏设计
框架: 各个界面: 用户: 登录注册:账号和密码(昵称) 主菜单:各种游戏,查看自己信息(积分,装备【游戏数据】),退出 游戏界面&#…...
[漏洞挖掘与防护] 04.Windows系统安全缺陷之5次Shift漏洞启动计算机机理分析
这是作者新开的一个专栏——“漏洞挖掘与防护”,前期会复现各种经典和最新漏洞,并总结防护技巧;后期尝试从零学习漏洞挖掘技术,包括Web漏洞和二进制及IOT相关漏洞,以及Fuzzing技术。新的征程,新的开启,漫漫长征路,偏向虎山行。享受过程,感谢您的陪伴,一起加油~ 欢迎关…...
手机极简待办app哪款好用?
在快节奏的现代生活中,我们常常需要处理大量的任务和信息,这时候一款好用的极简待办软件就显得尤为重要。它们不仅能帮助我们记录和管理待办事项,还能提高我们的工作效率和生活质量。 在众多的待办软件中,敬业签是一款非常受欢迎…...
SpringBoot高级-底层原理
目录 1 SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 02-SpringBoot2高级-自动化配置初体验 03-SpringBoot2高级-底层原理-Configuration配置注解 04-SpringBoot2高级-底层原理-Import注解使用1 05-SpringBoot2高级-底层原理-Import注解使用2 06-S…...
LabVIEW提高开发效率技巧----插入式架构
随着LabVIEW项目规模的扩大和系统复杂性的增加,传统的单一代码架构难以应对后期维护和功能扩展的需求。插入式架构(Plug-In Architecture)作为一种模块化设计方式,通过动态加载和运行子VI,使系统功能更加灵活、模块化&…...
MySQL COUNT(*)、COUNT(1)、COUNT(id)、COUNT(字段)效果及性能
文章目录 前言COUNT(exper)COUNT(*)优化COUNT(*) 与COUNT(1) COUNT(1)COUNT(id)COUNT(字段)总结参考 前言 业务开发中,我们经常要使用count做一些数据统计。今天根据MySQL5.7官方文档及丁奇老师的MySQL45讲,介绍一下COUNT(*)、COUNT(1)、COUNT(id)、COU…...
webpack4 - 动态导入文件 dynamic-import 报错的解决方法
介绍 webpack4动态导入文件报错,按照错误提示安装了插件,但未果。。 最后查到一个可行方案,记录如下。 1.通过懒加载的方式动态引入文件 const router new Router({routes: [{path: /home,name: Home,component: () >import(./views/h…...
【NodeJS】NodeJS+mongoDB在线版开发简单RestfulAPI (四):状态码的使用
本项目旨在学习如何快速使用 nodejs 开发后端api,并为以后开展其他项目的开启提供简易的后端模版。(非后端工程师) 由于文档是代码写完之后,为了记录项目中需要注意的技术点,因此文档的叙述方式并非开发顺序࿰…...
springboot061基于B2B平台的医疗病历交互系统(论文+源码)_kaic
摘 要 进入21世纪,计算机技术迅速向着网络化的、集成化方向发展。传统的单机版应用软件正在逐渐退出舞台,取而代之的是支持网络、支持多种数据信息的新一代网络版应用软件,形成了信息化的社会。信息化社会的形成和微电子技术日新月异的发展&…...
基于FFT + CNN -Transformer时域、频域特征融合的电能质量扰动识别模型
往期精彩内容: Python-电能质量扰动信号数据介绍与分类-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer的一…...
JAVA开发环境:IntelliJ IDEA、Java JDK、Maven 安装配置
一、安装IntelliJ IDEA 准备安装包 通过百度网盘分享的文件:idea2023.2U**.zip 链接:https://pan.baidu.com/s/1NB04A-jMXhZKsewYshGt-Q 提取码:oeft 安装 IntelliJ IDEA (1)、解压,安装文件如下&#…...
鸿蒙软件开发中常见的如何快速自动生成二维码?QRCode组件
QRCode 用于显示单个二维码的组件。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 二维码组件的像素点数量与内容有关,当组件尺寸过小时,可能出现无法展示内容的情况&…...
鸿蒙HarmonyOS NEXT 5.0开发(2)—— ArkUI布局组件
文章目录 布局Column:从上往下的布局Row:从左往右的布局Stack:堆叠布局Flex:自动换行或列 组件Swiper各种选择组件 华为官方教程B站视频教程 布局 主轴和交叉轴的概念: 对于Column布局而言,主轴是垂直方…...
【openGauss】OPENGAUSS/POSTGRESQL 中float类型到int类型的隐式转换
下面这条sql在oracle和POSTGRESQL/OPENGAUSS中的查询结果不一致 select cast(cast(0.5 as float) as integer);在oracle中返回1,在openGauss中返回0,咋一看好像是openGauss中使用了截断的方式,但是如果执行 select cast(cast(1.5 as float) as integ…...
Docker:安装 Syslog-ng 的技术指南
1、简述 Syslog-ng 是一种流行的日志管理工具,能够集中处理和分析日志。通过 Docker 安装 Syslog-ng 可以简化部署和管理过程。本文将介绍如何使用 Docker 安装 Syslog-ng,并提供一个 Java 示例来展示如何将日志发送到 Syslog-ng。 2、安装 2.1 创建…...
即插即用的3D神经元注意算法!
本文所涉及所有资源均在 传知代码平台 可获取。 目录 3D神经元注意力:为每一个神经元分配权重!(算法) 一、概述 二、研究背景 三、主要贡献 四、模型结构和代码 五、数据集介绍 六、性能展示 六、复现过程 七、运行过程 SimAM总结…...
FPGA 蜂鸣器 音乐播放器
点击: FPGA 蜂鸣器音乐播放器 基于FPGA的beep音乐播放器设计 FPGA(Field Programmable Gate Array)蜂鸣器音乐播放器是一个将FPGA编程用于控制蜂鸣器播放音乐的设备。下面是一个简单的实现步骤和思路: 一、硬件准备 FPGA开发板…...
前端-基础CSS总结常用
1.书写位置:title 标签下方添加 style 双标签,style 标签里面书写 CSS 代码。 <title>CSS 初体验</title> <style>/* 选择器 { } */p {/* CSS 属性 */color: red;} </style><p>体验 CSS</p> <link rel="stylesheet" href=…...
Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)
coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 一 标定板的制作生成标定的PDF文件PDF转为图像格式图像加载到仿真中 二 仿真场景设置加载机器人加载的控制dummy ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b48549d355d8441d8dfc20bc7ba7196…...
CSS常见面试题
🎯CSS常见面试题 1.CSS的盒模型2.CSS选择器的优先级3.隐藏元素的方法有哪些?4.px和rem的区别是什么?5.重绘排版有什么区别?6.让一个元素水平垂直居中的方式有哪些?7.CSS的哪些属性可以继承?哪些不可以继承&…...
ChatGPT实现旅游推荐微信小程序
随着旅游行业的快速发展,个性化推荐已成为提升用户体验的重要手段。通过AI技术,提供一个智能旅游推荐小程序,使用户能够轻松获取定制化的旅行建议。 项目概述 项目目标 开发一个AI旅游推荐小程序,基于用户输入的旅行偏好&#…...
基于单片机的智能小区门禁系统设计(论文+源码)
1总体架构 智能小区门禁系统以STM32单片机和WiFi技术为核心,STM32单片机作为主控单元,通过WiFi模块实现与手机APP的连接,构建整个门禁系统。系统硬件包括RFID模块、指纹识别模块、显示屏、按键以及继电器。通过RFID绑定IC卡、APP面部识别、指…...
stm32F103 实现呼吸灯效果
目录 硬件连接 软件实现步骤 初始化系统时钟。 配置 GPIO 引脚。 配置定时器以生成 PWM 信号。 在主循环中调整 PWM 占空比以实现呼吸效果。 示例代码 1. 初始化系统时钟 2. 配置 GPIO 引脚 3. 配置定时器以生成 PWM 信号 4. 在主循环中调整 PWM 占空比以实现呼吸效…...
SAP 为 Copilot Joule 增添协作功能
在最新的SAP TechEd大会上,SAP发布了一系列创新功能,旨在扩展其AI平台Joule的能力,同时推出了其他工具,以提高企业效率并为开发人员提供更多支持。这些创新不仅将推动AI驱动的业务转型,还将加强数据的利用和简化开发流…...
Node.js 模块化
1. 介绍 1.1 什么是模块化与模块 ? 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用 1.2 什…...
【部署篇】RabbitMq-03集群模式部署
一、准备主机 准备3台主机用于rabbitmq部署,文章中是在centos7上安装部署rabbitmq3.8通过文章中介绍的方式可以同样在centos8、centos9上部署,只需下载对应的版本进行相同的操作。 主机IP角色说明192.168.128.31种子节点192.168.128.32普通节点192.16…...
【硬啃Dash-Fastapi-Admin】03-requirements-pg.txt 速览
文章目录 dash2.18.1 纯Python的Web应用框架Python Dash库的功能介绍和用法示例功能介绍用法示例 Flask-Compress1.15 Flask响应数据的压缩功能介绍用法示例注意事项 feffery-antd-charts0.1.0rc5 数据可视化组件库功能介绍用法示例 feffery-antd-components0.3.8 Dash 第三方组…...
【CS常见问题】你用的是VS2019,最高支持.NET5.0,但是项目将.NET6.0设为目标无法运行,怎么办?
.NET版本问题 报错示例报错分析最简单的方法步骤 报错示例 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 NETSDK1045 当前 .NET SDK 不支持将 .NET 6.0 设置为目标。请将 .NET 5.0 或更低版本设置为目标,或使用支持 .NET 6.0 的 .NET SDK 版本。 ABFview C:\x…...
wordpress 首页显示文章列表/百度统计官网
ctrlf:搜索本文将内的字符串ctrlshiftf:搜索工程中所有文件中的字符串ctrlp:搜索文件,在工程中所有文件中vscode可以动态实时语法检测,还能安装很多插件,也能很强大的标识符跳转(比sourceInsigh…...
图片网站如何做百度排名/seo教程排名第一
2021新的一年,开启新的征程,回顾2020,真是太“南”了。 从年初各大厂裁员,竟然成为一件理所应当的事情,到四月份 GitHub 上“996.ICU” 引起了大家的共鸣。即使我们兢兢业业“996”,但依旧难以抵御 35 岁时…...
美国做调查网站/百度百度一下就知道
mybatis报错: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2 mybatis异常:太多结果集异常。 就是说期望获得一个结果,但是却查询到了两个或多个。 比…...
西安网站优化平台/免费发布广告
传送门 签到题,直接瞎模拟就行了。代码 转载于:https://www.cnblogs.com/ldxcaicai/p/10084919.html...
网站建设基本常识/百度小程序优化排名
一年一度的双十二如期而至,今天的你买买买了吗,下面小编给大家分享一个动态秒杀倒计时功能。效果图活动未开始开始购买活动结束时分秒$(function() {fn()function fn() {var timer setInterval(function() {var start 2019/03/13 00:00:00;var StartTi…...
常熟做网站公司排名/佛山百度关键词排名
下面是几个与oracle紧密相关的unix/linux内核参数,在安装数据库的时候,一般都需要根据实际情况进行调整。Init.ora Parameter Kernel Parameter db_block_buffers shmmax, shmall db_files(maxdatafiles) nfile, maxfiles large_pool_size shmmax…...