精通C++ STL(六):list的模拟实现

目录

类及其成员函数接口总览

结点类的模拟实现

  构造函数

迭代器类的模拟实现

  迭代器类存在的意义

  迭代器类的模板参数说明

  构造函数

  ++运算符的重载

  --运算符的重载

  ==运算符的重载

  !=运算符的重载

  *运算符的重载

  ->运算符的重载

list的模拟实现

  默认成员函数

        构造函数

        拷贝构造函数

        赋值运算符重载函数

        析构函数

  迭代器相关函数

        begin和end

  访问容器相关函数

        front和back

  插入、删除函数

        insert

        erase

        push_back和pop_back

        push_front和pop_front

  其他函数

        size

        resize

        clear

        empty

        swap


类及其成员函数接口总览

namespace RussLeo
{// 模拟实现 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;       // 节点类型别名typedef _list_iterator<T, Ref, Ptr> self;  // 迭代器自身类型别名// 构造函数_list_iterator(node* pnode);  // 初始化迭代器,指向节点// 各种运算符重载函数self operator++();    // 前置递增self operator--();    // 前置递减self operator++(int); // 后置递增self operator--(int); // 后置递减bool operator==(const self& s) const; // 相等比较bool operator!=(const self& s) const; // 不等比较Ref operator*();      // 解引用Ptr operator->();     // 成员访问// 成员变量node* _pnode; // **一个指向结点的指针**,用于迭代};// 模拟实现 listtemplate<class T>class list{public:typedef _list_node<T> node;      // 节点类型别名typedef _list_iterator<T, T&, T*> iterator;  // 迭代器类型别名typedef _list_iterator<T, const T&, const T*> const_iterator;  // 常量迭代器类型别名// 默认成员函数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(iterator pos);            // 删除指定位置的元素void push_back(const T& x);              // 在末尾添加元素void pop_back();                        // 删除末尾元素void push_front(const T& x);             // 在头部添加元素void pop_front();                       // 删除头部元素// 其他函数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,首先需要实现一个结点类。

        一个链表结点需要存储的信息包括:数据前一个结点的地址后一个结点的地址。因此,该结点类的成员变量就包括了数据、前驱指针和后继指针。

        对于结点类的成员函数,我们只需要实现一个 构造函数。这个构造函数用于根据给定的数据初始化结点。结点的释放则由 list 类的析构函数负责处理。

  构造函数

结点类的构造函数 直接根据所提供的数据来创建一个结点。构造出来的结点将具有以下特点:

  • 数据域:存储传入的数据。
  • 前驱指针后继指针:均初始化为空指针 (nullptr)。
// 构造函数
_list_node(const T& val = T()): _val(val)        // 初始化数据域为传入的值, _prev(nullptr)  // 初始化前驱指针为空, _next(nullptr)  // 初始化后继指针为空
{}

注意:若构造结点时未传入数据,则构造函数会使用 list 容器存储类型的默认构造函数 构造出的值作为传入数据。

迭代器类的模拟实现

  迭代器类存在的意义

        当我们模拟实现 stringvector 时,并没有提到需要实现一个迭代器类,这是因为 stringvector 的数据都存储在连续的内存空间中。我们可以通过原生指针来实现迭代器的功能,指针支持自增、自减和解引用等操作,因此可以直接用于访问和操作数据。

        然而,对于 list 类而言,它的内部结构与 stringvector 不同。list 通常采用链表结构来存储数据,这意味着每个元素并不是存储在一个连续的内存块中,而是通过指针链接在一起。因此,原生指针无法直接用于链表中的迭代,因为在链表中元素的存储位置并不连续。

        为了能够遍历和操作链表中的元素,我们需要实现一个专门的迭代器类,该类需要管理链表节点的连接关系,并支持类似于指针的操作,如自增、自减和解引用。 

迭代器的意义在于让使用者能够以统一的方式访问容器中的数据,而无需关心底层实现。

        对于 list 类,它的节点指针行为不符合标准迭代器的定义。为了使 list 的迭代器能够像 stringvector 的迭代器一样使用,我们需要对节点指针进行封装。通过重载节点指针的各种运算符,我们可以实现与原生指针类似的操作。例如,当使用 list 的迭代器进行自增操作时,实际上执行的是 p = p->next,但用户不需要知道底层的具体实现细节。

总结: list 迭代器类本质上是对节点指针的封装,通过重载运算符,使得节点指针的行为看起来像普通指针一样。例如,节点指针自增操作能够指向下一个节点。

  迭代器类的模板参数说明

        我们所实现的迭代器类的模板参数列表当中为什么有三个模板参数?

template<class T, class Ref, class Ptr>

        这里的模板参数 RefPtr 分别代表了引用类型指针类型。在 list 的模拟实现中,我们定义了两个迭代器类型: 

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

这表明迭代器类的模板参数 RefPtr 用于区分普通迭代器和 const 迭代器。具体来说:

  • 普通迭代器iterator)使用 T& 作为引用类型,T* 作为指针类型。
  • const 迭代器const_iterator)使用 const T& 作为引用类型,const T* 作为指针类型。

总结: 迭代器类的三个模板参数(TRefPtr)使得迭代器能够适应不同的引用和指针类型,从而可以很好地区分普通迭代器和 const 迭代器。如果只设计两个模板参数,无法实现对普通迭代器和 const 迭代器的有效区分。因此,使用三个模板参数可以确保迭代器在不同的使用场景下具有正确的行为和类型。

  构造函数

        迭代器类实际上是对节点指针的封装,它的主要成员变量就是一个节点指针。迭代器的构造函数直接使用传入的节点指针来初始化迭代器对象。

// 构造函数
_list_iterator(node* pnode): _pnode(pnode)  // 初始化节点指针
{}

说明: self 是当前迭代器对象的类型:  

typedef _list_iterator<T, Ref, Ptr> self;

  ++运算符的重载

        前置自增(++ 的作用是将迭代器指向下一个节点,并返回自增后的迭代器对象。实现如下:

// 前置自增
self& operator++()
{_pnode = _pnode->_next; // 让节点指针指向下一个节点return *this; // 返回自增后的迭代器对象
}

        后置自增(++ 的作用是返回自增前的迭代器对象,然后将迭代器指向下一个节点。实现如下: 

// 后置自增
self operator++(int)
{self tmp(*this); // 记录当前迭代器的状态_pnode = _pnode->_next; // 让节点指针指向下一个节点return tmp; // 返回自增前的迭代器对象
}

  --运算符的重载

        前置自减(-- 的作用是将迭代器指向前一个节点,并返回自减后的迭代器对象。实现如下:

// 前置自减
self& operator--()
{_pnode = _pnode->_prev; // 让节点指针指向前一个节点return *this; // 返回自减后的迭代器对象
}

        后置自减(-- 的作用是返回自减前的迭代器对象,然后将迭代器指向前一个节点。实现如下:

// 后置自减
self operator--(int)
{self tmp(*this); // 记录当前迭代器的状态_pnode = _pnode->_prev; // 让节点指针指向前一个节点return tmp; // 返回自减前的迭代器对象
}

  ==运算符的重载

        当使用 == 运算符比较两个迭代器时,我们实际上是检查这两个迭代器是否指向同一个位置。具体来说,就是判断这两个迭代器中的节点指针是否相同。实现代码如下:

// 比较两个迭代器是否相等
bool operator==(const self& s) const
{return _pnode == s._pnode; // 判断两个节点指针是否相同
}

  !=运算符的重载

        != 运算符 的作用是判断两个迭代器是否不相等。这可以通过检查这两个迭代器中的节点指针是否不同来实现。实现代码如下:

// 比较两个迭代器是否不相等
bool operator!=(const self& s) const
{return _pnode != s._pnode; // 判断两个节点指针是否不同
}

  *运算符的重载

        当使用解引用操作符(*)时,我们想要获取当前迭代器指向节点的数据。为了支持对数据的修改,解引用操作符应返回一个引用。实现代码如下:

// 解引用操作符,返回节点指针所指节点的数据
Ref operator*()
{return _pnode->_val; // 返回节点指针所指节点的数据
}

  ->运算符的重载

        -> 运算符 用于通过迭代器访问节点中的数据。

        当节点存储的不是内置类型而是自定义类型时。例如日期类,我们可能会使用 -> 运算符来访问对象的成员。为了实现这一点,我们需要重载 -> 运算符,使其返回节点数据的地址。实现代码如下:

// 访问节点数据的地址
Ptr operator->()
{return &_pnode->_val; // 返回节点指针所指节点数据的地址
}

示例:

list<Date> lt;
Date d1(2024, 8, 12);
Date d2(2002,11, 2);
Date d3(1949, 10, 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 语句实际上涉及两个箭头操作:

  1. 第一个箭头 pos-> 调用重载的 operator->,返回 Date* 的指针。
  2. 第二个箭头 Date*-> 访问 Date 对象的成员变量 _year

        解释:虽然需要两个箭头操作,但为了提高程序的可读性,编译器在实现时做了优化,使得只有一个箭头操作是可见的。这是因为编译器会自动处理 -> 操作的特殊情况,简化用户代码中的箭头访问。

list的模拟实现

  默认成员函数

        构造函数

        list 是一个带头双向循环链表。在构造一个 list 对象时,我们通常会创建一个头结点,并将其前驱指针和后继指针都指向自身。这样可以简化链表的操作,避免在插入和删除节点时需要处理空链表的特殊情况。

// 构造函数
list()
{_head = new node; // 申请一个头结点_head->_next = _head; // 头结点的后继指针指向自己_head->_prev = _head; // 头结点的前驱指针指向自己
}

        拷贝构造函数

        拷贝构造函数 用于根据现有 list 容器创建一个新的对象。首先创建一个头结点,并将其前驱指针和后继指针都指向自己。然后,将原容器中的数据通过遍历方式逐一尾插到新构造的容器中。实现代码如下:

// 拷贝构造函数
list(const list<T>& lt)
{_head = new node; // 申请一个头结点_head->_next = _head; // 头结点的后继指针指向自己_head->_prev = _head; // 头结点的前驱指针指向自己// 遍历原容器,将数据逐一尾插到新容器中for (const auto& e : lt){push_back(e); // 将容器 lt 中的数据一个个尾插到新构造的容器后面}
}

        赋值运算符重载函数

        赋值运算符重载 有两种写法:传统写法和现代写法。两者各有优缺点,具体如下:

传统写法中,我们首先清空原容器,然后将新容器的数据逐个尾插到当前容器中。实现代码如下:

// 传统写法
list<T>& operator=(const list<T>& lt)
{if (this != &lt) // 避免自己给自己赋值{clear(); // 清空容器for (const auto& e : lt){push_back(e); // 将容器 lt 中的数据一个个尾插到链表后面}}return *this; // 支持连续赋值
}

现代写法利用了编译器的拷贝构造函数,使用右值参数创建临时对象,然后通过 swap 函数交换当前对象和临时对象的内容。实现代码如下:

// 现代写法
list<T>& operator=(list<T> lt) // 编译器接收右值时自动调用拷贝构造函数
{swap(lt); // 交换这两个对象return *this; // 支持连续赋值
}

总结

  • 传统写法 更为直接,但需要额外的清理和插入操作。
  • 现代写法 更简洁,通过 swap 函数避免了显式的清理操作,利用了资源管理的自动化机制。

        析构函数

        析构函数 在对象销毁时负责清理容器中的数据,释放头结点,并将头指针置空。实现代码如下:

// 析构函数
~list()
{clear(); // 清理容器中的所有数据delete _head; // 释放头结点_head = nullptr; // 将头指针置空
}

  迭代器相关函数

        begin和end

        beginend 函数 的实现用于获取迭代器,分别指向容器的第一个有效数据和最后一个有效数据的下一个位置。以下是其实现代码:

普通迭代器

  • begin() 返回指向第一个有效数据的迭代器,即头结点后一个结点的地址。
  • end() 返回指向最后一个有效数据的下一个位置的迭代器,即头结点的地址。
    // 返回指向第一个有效数据的迭代器
    iterator begin()
    {return iterator(_head->_next); // 使用头结点后一个结点的地址构造普通迭代器
    }// 返回指向最后一个有效数据的下一个位置的迭代器
    iterator end()
    {return iterator(_head); // 使用头结点的地址构造普通迭代器
    }
    

常量迭代器

  • begin() const 返回指向第一个有效数据的常量迭代器,即头结点后一个结点的地址。
  • end() const 返回指向最后一个有效数据的下一个位置的常量迭代器,即头结点的地址。
// 返回指向第一个有效数据的常量迭代器
const_iterator begin() const
{return const_iterator(_head->_next); // 使用头结点后一个结点的地址构造常量迭代器
}// 返回指向最后一个有效数据的下一个位置的常量迭代器
const_iterator end() const
{return const_iterator(_head); // 使用头结点的地址构造常量迭代器
}

说明

  • iteratorconst_iterator 分别用于非 const 和 const 对象。
  • begin 函数返回的是指向第一个实际数据的迭代器,end 函数返回的是指向“末尾”的迭代器,通常用来表示容器的结束位置。

  访问容器相关函数

        front和back

        frontback 函数 用于获取链表中第一个有效数据和最后一个有效数据的引用。实现时需要处理普通对象和常量对象的情况。以下是其实现代码:

普通对象

  • front() 返回第一个有效数据的引用。
  • back() 返回最后一个有效数据的引用。
// 返回第一个有效数据的引用
T& front()
{return *begin(); // 通过 begin() 获取第一个有效数据的引用
}// 返回最后一个有效数据的引用
T& back()
{return *(--end()); // 通过 --end() 获取最后一个有效数据的引用
}

常量对象

  • front() const 返回第一个有效数据的常量引用。
  • back() const 返回最后一个有效数据的常量引用。
// 返回第一个有效数据的常量引用
const T& front() const
{return *begin(); // 通过 begin() 获取第一个有效数据的常量引用
}// 返回最后一个有效数据的常量引用
const T& back() const
{return *(--end()); // 通过 --end() 获取最后一个有效数据的常量引用
}

说明

  • front() 函数返回第一个有效数据的引用或常量引用,back() 函数返回最后一个有效数据的引用或常量引用。
  • --end() 操作将 end() 迭代器向前移动一个位置,指向最后一个有效数据。

  插入、删除函数

        insert

        insert 函数 用于在给定迭代器的位置之前插入一个新结点。

        实现步骤包括定位插入位置、调整指针以及插入新结点。以下是 insert 函数的实现代码:

// 在给定迭代器位置之前插入一个新结点
void insert(iterator pos, const T& x)
{assert(pos._pnode); // 检测 pos 的合法性,确保迭代器指向有效的结点node* cur = pos._pnode; // 取得迭代器 pos 所指结点的指针node* prev = cur->_prev; // 取得 pos 前一个位置的结点指针node* newnode = new node(x); // 根据所给数据 x 构造一个新的结点// 建立 newnode 与 cur 之间的双向关系newnode->_next = cur;cur->_prev = newnode;// 建立 newnode 与 prev 之间的双向关系newnode->_prev = prev;prev->_next = newnode;
}

        erase

        erase 函数 用于删除给定迭代器位置的结点。

        实现步骤包括获取当前结点及其相邻结点的指针,调整相邻结点的双向关系,然后释放当前结点的内存。

// 删除所给迭代器位置的结点
iterator erase(iterator pos)
{assert(pos._pnode); // 检测 pos 的合法性,确保它指向一个有效结点assert(pos != end()); // 删除的结点不能是头结点node* cur = pos._pnode; // 取得要删除的结点指针node* prev = cur->_prev; // 取得前一个结点指针node* next = cur->_next; // 取得后一个结点指针delete cur; // 释放 cur 结点的内存// 建立 prev 与 next 之间的双向关系prev->_next = next;next->_prev = prev;return iterator(next); // 返回删除结点后的下一个迭代器
}

        push_back和pop_back

        push_backpop_back 函数的实现用于 list 的尾插和尾删操作。

        在已经实现了 inserterase 函数的情况下,可以通过复用这两个函数来实现 push_backpop_back。以下是这两个函数的实现代码及说明:

// 尾插函数
void push_back(const T& x)
{insert(end(), x); // 在头结点前插入新结点
}// 尾删函数
void pop_back()
{erase(--end()); // 删除头结点的前一个结点
}

        push_front和pop_front

        push_frontpop_front 函数用于 list 的头插和头删操作。

        可以通过复用已经实现的 inserterase 函数来实现这两个功能。以下是这两个函数的实现代码及说明:

// 头插函数
void push_front(const T& x)
{insert(begin(), x); // 在第一个有效结点前插入新结点
}// 头删函数
void pop_front()
{erase(begin()); // 删除第一个有效结点
}

  其他函数

        size

        size 函数用于获取当前容器中的有效数据个数。

        由于 list 是链表,无法直接通过索引访问元素,所以需要通过遍历的方式逐个统计有效数据的个数。以下是 size 函数的实现代码及说明:

// 获取当前容器中有效数据的个数
size_t size() const
{size_t sz = 0; // 用于统计有效数据的个数const_iterator it = begin(); // 获取第一个有效数据的迭代器while (it != end()) // 遍历直到容器的末尾{sz++; // 计数器加一it++; // 迭代器前移到下一个结点}return sz; // 返回有效数据的个数
}

        resize

        resize 函数用于调整 list 容器的大小,以适应给定的大小 n

根据当前容器的大小和目标大小 nresize 的规则如下:

  1. 若当前容器的大小小于 n
    • 尾部添加元素,直到容器大小等于 n
  2. 若当前容器的大小大于 n
    • 只保留前 n 个元素,删除其余元素。

        实现 resize 时避免直接调用 size 函数,因为 size 函数会遍历整个容器,而我们希望在遍历过程中完成调整大小的操作。以下是 resize 函数的实现代码及说明:

// 调整容器的大小为n,如果当前大小小于n则尾插,若当前大小大于n则删除多余元素
void resize(size_t n, const T& val = T())
{iterator i = begin(); // 获取第一个有效数据的迭代器size_t len = 0; // 记录当前遍历的数据个数// 遍历容器,直到len达到n或遍历完所有元素while (len < n && i != end()){len++; // 增加计数器i++; // 迭代器前移}if (len == n) // 如果当前有效数据个数已达到或超过n{// 删除从第n个元素开始的所有元素while (i != end()){i = erase(i); // 删除当前元素并更新迭代器}}else // 当前有效数据个数小于n{// 尾插元素直到容器大小达到nwhile (len < n){push_back(val); // 尾插元素len++; // 增加计数器}}
}

        clear

        clear 函数会逐个删除所有结点,最终只保留头结点。

// 清空容器,逐个删除所有结点,只保留头结点
void clear()
{iterator it = begin(); // 获取第一个有效数据的迭代器while (it != end()) // 逐个删除结点,直到遍历完所有有效数据{it = erase(it); // 删除当前结点并更新迭代器}
}

        empty

        empty函数用于判断容器是否为空。我们可以通过比较容器的begin函数和end函数返回的迭代器来实现这个判断。如果beginend返回的迭代器相同,那么说明容器中只有一个头结点,即容器为空。

// empty函数用于判断容器是否为空
bool empty() const
{return begin() == end(); // 判断begin和end返回的迭代器是否是同一个位置的迭代器,若相同则说明容器中只有头结点
}

        swap

        swap函数用于交换两个容器。在list容器中,实际上存储的仅仅是链表的头指针,因此我们只需交换两个容器的头指针即可实现交换操作。

// 交换当前容器和指定容器的内容
void swap(list<T>& lt)
{::swap(_head, lt._head); // 交换两个容器的头指针
}

注意: 在此处调用库中的 swap 函数时,需要加上 ::(作用域限定符),以确保编译器在全局范围内查找 swap 函数,避免编译器错误地调用当前类中的 swap 函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397727.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣 | 递增子序列 | 动态规划 | 最长递增子序列、最长递增子序列的个数、及其变式

文章目录 一、300. 最长递增子序列二、673. 最长递增子序列的个数三、变式1、646. 最长数对链2、1218. 最长定差子序列3、1027. 最长等差数列4、354. 俄罗斯套娃信封问题5、1964. 找出到每个位置为止最长的有效障碍赛跑路线 最长递增子序列&#xff1a;原序-递增数值问题 最长定…

无人机之陀螺仪篇

陀螺仪器最早是用于航海导航&#xff0c;但随着科学技术的发展&#xff0c;它在航空和航天事业中也得到广泛的应用。陀螺仪不仅可以作为指示仪表&#xff0c;而更重要的是它可以作为自动控制系统中的一个敏感元件&#xff0c;即可作为信号传感器。 根据需要&#xff0c;陀螺仪器…

使用字典优化命令行参数处理

在 Python 中&#xff0c;可以使用字典优化命令行参数的处理。特别是在处理多个选项或参数时&#xff0c;使用字典可以使代码更加简洁和易于维护。以下是一个使用 argparse 模块和字典来管理命令行参数的示例。 问题背景 在一个项目中&#xff0c;您需要根据用户通过命令行参数…

yolov5更换MobileNetV3的主干网络

目录 1.定义网络 2.将test_MobileNetV3.py上面的代码复制粘贴到如下文件里面 2.1复制需要更改的原来的网络如何改结构 3.更改yolo.py注册网络 1.定义网络 1. 宽度为0.5的YOLOV5网络的结构图 在主干网络上面可以重新定义成三层&#xff0c;编号从0开始 如图是MobileNetV3 的…

掌握7步网站设计流程,提升你的网站用户体验

在当代社会&#xff0c;网站制作已经成为不可或缺的一部分。随着互联网的发展&#xff0c;越来越多的人开始使用网站浏览器来获取信息、交流和消费。一个好的网站设计可以吸引用户的注意力&#xff0c;提高用户体验&#xff0c;从而提高用户的满意度和忠诚度。那么&#xff0c;…

网络剪枝——network-slimming 项目复现

目录 文章目录 目录网络剪枝——network-slimming 项目复现clone 存储库Baselinevgg训练结果 resnet训练结果 densenet训练结果 Sparsityvgg训练结果 resnet训练结果 densenet训练结果 Prunevgg命令结果 resnet命令结果 densenet命令结果 Fine-tunevgg训练结果 resnet训练结果 …

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——5.string

1.字符串相乘 . - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 1.如果两个串有一个首元素为‘0’&#xff0c;则直接返回‘0’ 2.设置两层循环&#xff0c;内层第一次循环 用于str插入初始数据 &#xff08;num2 的各个元素和num1 的最后一个元素相乘的结果&#…

C代码做底层及Matlab_SimuLink做应用层设计单片机程序

前言:SimuLink工具极其强大,但是能直接支持单片机自主开发的很少,造成这个问题的原因主要是我们使用的芯片底层多是C代码工程,芯片厂家也只提供C代码库,很少能提供SimuLink的支持库,即使提供也不是很不完善,如NXP的一些芯片提供的SimuLink库不含盖高级应用,再比如意法半…

哈希表 - 快乐数

202. 快乐数 方法一&#xff1a;用哈希集合检测循环 /*** param {number} n* return {boolean}*/let getNext function(n) {return n.toString().split().map(i > i ** 2).reduce((a, b) > a b); }let isHappy function(n) {let seen new Set();while (n ! 1 &&…

什么是跨境电商独立站?为什么选择做独立站?

独立站在近两年被推上风口&#xff0c;很多人跟风涌入赛道&#xff0c;但并不知道做独立站的根本原因是什么&#xff1f;为什么跨境电商要做独立站&#xff1f; 今天分享这篇文章&#xff0c;希望能帮助正在建站或想要建站的朋友们建立起对独立站的基本认知&#xff0c;做到不踩…

【学习笔记】Matlab和python双语言的学习(图论最短路径)

文章目录 前言一、图论基本概念示例 二、代码实现----Matlab三、代码实现----python总结 前言 通过模型算法&#xff0c;熟练对Matlab和python的应用。 学习视频链接&#xff1a; https://www.bilibili.com/video/BV1EK41187QF?p36&vd_source67471d3a1b4f517b7a7964093e6…

Java线程模型

一、相关知识 用户级线程&#xff08;ULT&#xff09;&#xff1a;实现在用户空间的线程称为用户级线程。用户线程是完全建立在用户空间的线程库&#xff0c;用户线程的创建、调度、同步和销毁全由用户空间的库函数完成&#xff0c;不需要内核的参与&#xff0c;也不需要进行用…

FPGA之间数据传输的讨论:解析数据传输与同步技术

在现代电子工程领域&#xff0c;数据传输和同步技术是确保信息准确、高效传递的关键。FPGA间的高速数据传输是实现复杂系统功能的关键技术之一。本文将基于移知公开课《FPGA之间数据传输的讨论》的内容&#xff0c;探讨FPGA间数据传输的技术细节和面临的挑战&#xff0c;帮助读…

使用VS2022生成安装包

首先需要本地已经能够正常运行的软件包&#xff0c;包含可执行文件及必要的运行库等&#xff0c;如下所示RemoteCli.exe为最终的可执行文件 打开VS2022 &#xff0c;选择 扩展–>管理扩展–>联机&#xff0c;搜索Microsoft Visual Studio Installer Projects&#xff0c;…

Lua调用c#

1. 类 --lua中使用C#的类非常简单 --固定套路 --CS.命名空间.类名 --Unity的类 比如 GameObject Transform等等 —— CS.UnityEngine.类名 --CS.UnityEngine.GameObject--通过C#中的类 实例化一个对象 lua中没有new 所以我们直接 类名括号就是实例化对象 --默认调用的 相当于就…

智能分析/视频汇聚EasyCVR安防视频融合管理云平台技术优势分析

安防行业的发展历程主要围绕视频监控技术的不断改革升级&#xff0c;从最初的模拟监控到数字监控&#xff0c;再到高清化、网络化监控&#xff0c;直至现在的智能化监控&#xff0c;每一次变革都推动了行业的快速发展。特别是近年来&#xff0c;随着AI、大数据、物联网等技术的…

LVS负载均衡(twenty-six day)

一、LVS &#xff08;一&#xff09;什么是LVS linux virtural server的简称&#xff0c;也就是linxu虚拟机服务器&#xff0c;这是一个由章文岩博士发起的开源项目&#xff0c;官网是http://www.linuxvirtualserver.org,现在lvs已经是linux内核标准的-部分&#xff0c;使用lv…

学术周交流与学习节选

文章目录 1、粒度多模态运动分析1.1 免特征重建的终身行人重识别1.2 无样本保留的终身行人重识别1.3 粒度多模态运动之类增量学习1.4 粒度多模态之人体姿态估计扩散模型 2、深度伪造的被动取证与主动防御2.1 研究现状及主要方法2.2 基于梯度的伪影特征表示2.3 基于伪造自适应学…

SQL注入实例(sqli-labs/less-18)

0、初始页面 先使用brup爆破密码&#xff0c;账号admin&#xff0c;密码admin 1、确定闭合字符 判断注入点在post请求参数的User-agent处 闭合字符为单引号 2、爆库名 3、爆表名 4、爆列名 5、查询最终目标 在index.php中有这么一句 $insert"INSERT INTO security.uage…

haproxy算法与具体实现

一、负载均衡 1.什么是负载均衡 负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均 衡将特定的业务(web服务、网络流量等)分担给指定的一个或多个后端特定的服务器或设备&#xff0c;从…