当前位置: 首页 > news >正文

【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的析构函数来实现。

classstruct关键字选择:如果不想用访问限定符来限制成员的访问,所有的成员都会用structstruct默认是公有protectedclass默认是私有private。这里选择使用structlist中会频繁地访问成员变量,想要成员变量都是公有的,就可以使用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.迭代器类的由来

之前stringvector实现时都没有实现过迭代器类,为什么这里要实现一个迭代器类呢?

原因:stringvector对象都将他们的数据存储在了一块连续的内存空间,我们通过自增、自减以及解引用操作就可以对相应位置的数据进行一系列操作。因此stringvector当中的迭代器就是原生指针。

template<class T>
class vector
{
public:typedef T* iterator;// vector的迭代器就是原生指针
};

但是list容器中的各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅仅通过结点指针的自增自减解引用等操作对相应的结点的数据进行操作。而迭代器的意义就是:让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行操作。

画板

既然list的结点指针的行为不满足迭代器的定义,我们可以对这个结点的指针进行封装,然后对结点指针的各种运算符操作进行重载,让我们可以用和stringvector中的迭代器一样的方式使用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操作中,我们可以知道,TRefPtr分别代表的是类型、引用类型和指针类型。

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用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.赋值运算符的重载

  1. 传统写法:先调用clear函数将原容器清空,然后将容器lt当中的数据,通过遍历的方式一个个尾插到清空厚的容器当中。
// 赋值运算符的重载的传统写法:
list<T> operator=(const list<T> lt)
{if (this != &lt)// 避免自己给自己赋值{clear();// 清空容器for (const auto& e : lt){push_back(e);// 将容器中的数据一个个尾插到链表后面}}return  *this;
}
  1. 现代写法:首先利用编译器机制,故意不是用引用接收参数,通过编译器哦自动调用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函数:

  1. 若当前容器的size晓瑜所给的n,则尾插结点知道size为n。
  2. 若当前容器的size大于所给n,则只保留前n个有效数据。

实现resize的方法是:设置一个变量len,用来记录当前所遍历的数据个数,然后开始遍历容器。在遍历的过程中如果:

  1. len大于或者等于n时遍历结束,释放掉该结点厚的所有结点。
  2. 容器遍历完成同时遍历结束,此时说明容器中的有效数据个数小于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 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、三个类与成员函数接口 在list.…...

以30个面试问题和案例为导向:全面解析 Java Servlet是什么?基本概念、实现原理、生命周期、类结构、请求与响应的处理机制,以及性能优化和安全性管理

Servlet 是 Java Web 开发的核心组件之一&#xff0c;负责处理客户端请求并生成动态响应。本文将深入探讨 Servlet 的基本概念、实现原理、生命周期、类结构、请求与响应的处理机制&#xff0c;以及性能优化和安全性管理&#xff0c;帮助开发者从多方面掌握 Servlet。 文章目录…...

MFC小游戏设计

框架&#xff1a; 各个界面&#xff1a; 用户&#xff1a; 登录注册&#xff1a;账号和密码&#xff08;昵称&#xff09; 主菜单&#xff1a;各种游戏&#xff0c;查看自己信息&#xff08;积分&#xff0c;装备【游戏数据】&#xff09;&#xff0c;退出 游戏界面&#…...

[漏洞挖掘与防护] 04.Windows系统安全缺陷之5次Shift漏洞启动计算机机理分析

这是作者新开的一个专栏——“漏洞挖掘与防护”,前期会复现各种经典和最新漏洞,并总结防护技巧;后期尝试从零学习漏洞挖掘技术,包括Web漏洞和二进制及IOT相关漏洞,以及Fuzzing技术。新的征程,新的开启,漫漫长征路,偏向虎山行。享受过程,感谢您的陪伴,一起加油~ 欢迎关…...

​手机极简待办app哪款好用?

在快节奏的现代生活中&#xff0c;我们常常需要处理大量的任务和信息&#xff0c;这时候一款好用的极简待办软件就显得尤为重要。它们不仅能帮助我们记录和管理待办事项&#xff0c;还能提高我们的工作效率和生活质量。 在众多的待办软件中&#xff0c;敬业签是一款非常受欢迎…...

SpringBoot高级-底层原理

目录 1 SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 02-SpringBoot2高级-自动化配置初体验 03-SpringBoot2高级-底层原理-Configuration配置注解 04-SpringBoot2高级-底层原理-Import注解使用1 05-SpringBoot2高级-底层原理-Import注解使用2 06-S…...

LabVIEW提高开发效率技巧----插入式架构

随着LabVIEW项目规模的扩大和系统复杂性的增加&#xff0c;传统的单一代码架构难以应对后期维护和功能扩展的需求。插入式架构&#xff08;Plug-In Architecture&#xff09;作为一种模块化设计方式&#xff0c;通过动态加载和运行子VI&#xff0c;使系统功能更加灵活、模块化&…...

MySQL COUNT(*)、COUNT(1)、COUNT(id)、COUNT(字段)效果及性能

文章目录 前言COUNT(exper)COUNT(*)优化COUNT(*) 与COUNT(1) COUNT(1)COUNT(id)COUNT(字段)总结参考 前言 业务开发中&#xff0c;我们经常要使用count做一些数据统计。今天根据MySQL5.7官方文档及丁奇老师的MySQL45讲&#xff0c;介绍一下COUNT(*)、COUNT(1)、COUNT(id)、COU…...

webpack4 - 动态导入文件 dynamic-import 报错的解决方法

介绍 webpack4动态导入文件报错&#xff0c;按照错误提示安装了插件&#xff0c;但未果。。 最后查到一个可行方案&#xff0c;记录如下。 1.通过懒加载的方式动态引入文件 const router new Router({routes: [{path: /home,name: Home,component: () >import(./views/h…...

【NodeJS】NodeJS+mongoDB在线版开发简单RestfulAPI (四):状态码的使用

本项目旨在学习如何快速使用 nodejs 开发后端api&#xff0c;并为以后开展其他项目的开启提供简易的后端模版。&#xff08;非后端工程师&#xff09; 由于文档是代码写完之后&#xff0c;为了记录项目中需要注意的技术点&#xff0c;因此文档的叙述方式并非开发顺序&#xff0…...

springboot061基于B2B平台的医疗病历交互系统(论文+源码)_kaic

摘 要 进入21世纪&#xff0c;计算机技术迅速向着网络化的、集成化方向发展。传统的单机版应用软件正在逐渐退出舞台&#xff0c;取而代之的是支持网络、支持多种数据信息的新一代网络版应用软件&#xff0c;形成了信息化的社会。信息化社会的形成和微电子技术日新月异的发展&…...

基于FFT + CNN -Transformer时域、频域特征融合的电能质量扰动识别模型

往期精彩内容&#xff1a; Python-电能质量扰动信号数据介绍与分类-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer的一…...

JAVA开发环境:IntelliJ IDEA、Java JDK、Maven 安装配置

一、安装IntelliJ IDEA 准备安装包 通过百度网盘分享的文件&#xff1a;idea2023.2U**.zip 链接&#xff1a;https://pan.baidu.com/s/1NB04A-jMXhZKsewYshGt-Q 提取码&#xff1a;oeft 安装 IntelliJ IDEA &#xff08;1&#xff09;、解压&#xff0c;安装文件如下&#…...

鸿蒙软件开发中常见的如何快速自动生成二维码?QRCode组件

QRCode 用于显示单个二维码的组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 二维码组件的像素点数量与内容有关&#xff0c;当组件尺寸过小时&#xff0c;可能出现无法展示内容的情况&…...

鸿蒙HarmonyOS NEXT 5.0开发(2)—— ArkUI布局组件

文章目录 布局Column&#xff1a;从上往下的布局Row&#xff1a;从左往右的布局Stack&#xff1a;堆叠布局Flex&#xff1a;自动换行或列 组件Swiper各种选择组件 华为官方教程B站视频教程 布局 主轴和交叉轴的概念&#xff1a; 对于Column布局而言&#xff0c;主轴是垂直方…...

【openGauss】OPENGAUSS/POSTGRESQL 中float类型到int类型的隐式转换

下面这条sql在oracle和POSTGRESQL/OPENGAUSS中的查询结果不一致 select cast(cast(0.5 as float) as integer);在oracle中返回1&#xff0c;在openGauss中返回0&#xff0c;咋一看好像是openGauss中使用了截断的方式,但是如果执行 select cast(cast(1.5 as float) as integ…...

Docker:安装 Syslog-ng 的技术指南

1、简述 Syslog-ng 是一种流行的日志管理工具&#xff0c;能够集中处理和分析日志。通过 Docker 安装 Syslog-ng 可以简化部署和管理过程。本文将介绍如何使用 Docker 安装 Syslog-ng&#xff0c;并提供一个 Java 示例来展示如何将日志发送到 Syslog-ng。 2、安装 2.1 创建…...

即插即用的3D神经元注意算法!

本文所涉及所有资源均在 传知代码平台 可获取。 目录 3D神经元注意力&#xff1a;为每一个神经元分配权重&#xff01;&#xff08;算法&#xff09; 一、概述 二、研究背景 三、主要贡献 四、模型结构和代码 五、数据集介绍 六、性能展示 六、复现过程 七、运行过程 SimAM总结…...

FPGA 蜂鸣器 音乐播放器

点击&#xff1a; FPGA 蜂鸣器音乐播放器 基于FPGA的beep音乐播放器设计 FPGA&#xff08;Field Programmable Gate Array&#xff09;蜂鸣器音乐播放器是一个将FPGA编程用于控制蜂鸣器播放音乐的设备。下面是一个简单的实现步骤和思路&#xff1a; 一、硬件准备 FPGA开发板…...

前端-基础CSS总结常用

1.书写位置:title 标签下方添加 style 双标签,style 标签里面书写 CSS 代码。 <title>CSS 初体验</title> <style>/* 选择器 { } */p {/* CSS 属性 */color: red;} </style><p>体验 CSS</p> <link rel="stylesheet" href=…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...