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

C++STL——容器-list(含模拟实现,即底层原理)(含迭代器失效问题)(所有你不理解的问题,这里都有解答,最详细)

目录

1.迭代器的分类

2.list的使用

2.1  list的构造

2.2  list iterator

2.3  list capacity

2.4  list element access

​编辑 

2.5  list modifiers

​编辑2.5.1   list插入和删除

2.5.2  insert /erase

2.5.3  resize/swap/clear

 ​编辑

2.6  list的一些其他接口

2.6.1  splice

2.6.2  remove

2.6.3  sort

3.list的模拟实现

4.最后一个问题

 

 


1.迭代器的分类

 

这里需要说明一些东西:

1.就是下面的是上面的子类:比如双向迭代器是单向迭代器的子类(这个父子类的判断,跟迭代器的功能可没关系,不要说双向迭代器的功能比单向多,就说单向是双向的子类,不是这么来判断的)。 

2.还有就是看上面的第二张图,越往后,迭代器的功能就越多,而且,都是对上一个迭代器的功能进行了继承并拓展。比如:双向迭代器的功能有++/--,而单向迭代器有++,双向迭代器是不是继承了单向迭代器的功能,并实现了拓展?是的!

2.list的使用

2.1  list的构造

list<int> l1; // 构造空的l1

list<int> l2(4, 100); // l2中放4个值为100的元素

list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3

list<int> l4(l3); // 用l3拷贝构造l4

list<int> l6={ 1,2,3,4,5 };//初始化列表初始化l6

2.2  list iterator

其实,对于lterator,也就跟前面的那几个一样使用就行,只不过,这里需要注意的是,vector是原生态指针,而list是对原生态指针(节点指针)进行封装,(模板),后面的模拟实现会讲。

注意:遍历链表只能用迭代器和范围for 

2.3  list capacity

2.4  list element access

 

2.5  list modifiers

2.5.1   list插入和删除

 push_back/pop_back/push_front/pop_front

2.5.2  insert /erase

2.5.3  resize/swap/clear

 

2.6  list的一些其他接口

2.6.1  splice

C++11相对于C++98来说增加了一个"模板特化",这个东西咱们后面会出一期模板专题,专门来讲这个模板。那么接下来先不看模板特化,先看引用。

这个接口的作用是将一个链表中的数据裁剪(裁剪后,原链表中的数据将消失),到另一个链表中。接下来看官方库中的代码例子来理解:

std::list<int> mylist1, mylist2;std::list<int>::iterator it;// set some initial values:for (int i=1; i<=4; ++i)mylist1.push_back(i);      // mylist1: 1 2 3 4for (int i=1; i<=3; ++i)mylist2.push_back(i*10);   // mylist2: 10 20 30it = mylist1.begin();++it;                         // points to 2mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4// mylist2 (empty)// "it" still points to 2 (the 5th element)mylist2.splice (mylist2.begin(),mylist1, it);// mylist1: 1 10 20 30 3 4// mylist2: 2// "it" is now invalid.it = mylist1.begin();std::advance(it,3);           // "it" points now to 30mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());// mylist1: 30 3 4 1 10 20

直接来看mylist1.splice ,这个的意思是将mylist2中的数据剪裁到 it指向的那个数据之前的位置,并将原mylist2中的数据清零。

第二个:将mylist1中的原it指向的数据(2),剪裁到mylist2的begin()指向的位置。

第三个:将mylist1中的从it位置开始,end()结束的数据(30,3,4)剪裁到begin()的位置。

2.6.2  remove

void remove (const value_type& val);

删除链表中值为val的数据。

2.6.3  sort

其实list中的sort无法使用算法库中的sort,因为算法库中的sort实现是靠+/-来实现,但是list迭代器不支持+/-,而且,相对于vector来说,list的sort排序,特别对于一些特别大的数据来说,效率 会很低。

3.list的模拟实现

其实,list的模拟实现还是比较难的,咱们以前模拟实现的vector,string难在它的实现上面,而list的模拟实现难在它的实现框架上面,那么下面让博主来帮助你们理解吧。

 

这个分为三个重要的模板。

1.一个是对于链表中的节点的模板(因为数据T可能有不同的类型)

2.一个是对于迭代器实现的模板,那这时候就要有人问了:以前的vector不是没有迭代器实现模板吗?这不一样,list的迭代器是对原生态指针的封装,就是一个模板。而且,在这里的iterator相当于是类了,就是好像不再是指针,但还是具有指针的效果。 

3.最后一个就是整个的list链表它也是一个模板。

那么接下来让咱们来看看模板吧,等会我会把为什么这么写的目的,以及细节全部讲透。

//这是第一个类模板

template<class T>

struct list_node

{

        list_node* _next;

        list_node* _prev;

        T _data;//注意,这里的_data是模板数据,后面会用到这个知识点。

        list_node(const T& x = T())//初始化

        :_next(nullptr)

        ,_prev(nullptr)

        ,_data(x)

        {}

};

//这是第二个类模板

// typedef list_iterator<T, T&, T*> iterator;

// typedef list_iterator<T, const T&, const T*> const_iterator;

template<class T, class Ref, class Ptr>

struct list_iterator

{

        typedef list_node<T> Node;

        typedef list_iterator<T, Ref, Ptr> Self;

        Node* _node;

        list_iterator(Node* node)//初始化

        :_node(node)

        {}

};

//这是第三个类模板

template<class T>

class list

{

        typedef list_node<T> Node;

        public:

        typedef list_iterator<T, T&, T*> iterator;

        //typedef list_const_iterator<T> const_iterator;

        typedef list_iterator<T, const T&, const T*> const_iterator;

List()    //初始化
{
    _head = new Node;
    _head->_prev = _head;
    _head->_next = _head;
    _size = 0;
}

private:

        Node* _head;

        size_t _size;

};

每一个类或者类模板写出来后,一定要记得初始化,这里需要注意一点,也是我写代码的时候发现的:当你写一个类的时候,一定要注意,这个类的构造尤其重要,就比如我这个,我如果说不写这个构造的话,那么编译器会默认构造,那么_head就是空,你是在end()位置插入,也就是_head位置,你的_head还是空,当你的pos._node获得节点的时候,这个还是空呢,是访问不了任何成员的。因为下面要用到cur去访问,所以记住,空节点访问不了任何成员 。

这个注意事项涉及到insert的模拟实现的,所以在这里咱们先把insert的模拟实现写了。

这里涉及到几个老生常谈的问题:

1.连接链表的时候的顺序问题,一般先将新插入的链表连接好,(当然,你要是说先将各个要用到的节点重新定义一下,那就不要管顺序的问题了),顺序的问题就是为了避免有找不到节点的情况出现。

2.这里的pos是迭代器类型,不是说迭代器是指针嘛?怎么访问它的成员变量不应该用->嘛?怎么用"."呢?这个问题,咱们后面跟另外一个问题一起将。 (标题:最后一个问题处)

好,那么咱们接下来接着往后看模拟实现:

第一个类模板已经实现完了,接下来看第二个类模板的实现:

// typedef list_iterator<T, T&, T*> iterator;

// typedef list_iterator<T, const T&, const T*> const_iterator;

template<class T,class Ref,class Ptr>
struct list_iterator
{
    typedef list_node<T> Node;
    typedef list_iterator<T, Ref, Ptr> Self;
    Node* _node;//定义一个节点

    list_iterator(Node* node)
        :_node(node)//初始化那个节点
    {}
    Ref operator*()
    {
        return _node->_data;
    }
    Ptr operator->()
    {
        return &_node->_data;
    }
    //前置++
    Self& operator++()
    {
        _node = _node->_next;
        return *this;
    }
    //后置++
    Self operator++(int)
    {
        Self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    //前置--
    Self& operator--()
    {
        _node = _node->_prev;
        return *this;
    }
    //后置--
    Self operator--(int)
    {
        Self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    bool operator!=(const Self &s) const//lt.end()这是个迭代器类型啊,肯定是Self&,怎么能是List<T>呢?
    {
        return _node != s._node;
    }
    bool operator==(const Self& s) const
    {
        return _node == s._node;
    }

};

这里由于放图片,代码太长,影响观看,所以直接用块引用。 

1.这个T,Ref,Ptr 都是指代的什么东西呀?首先,这是一个类模板,所以说,这三个肯定都是指代的一类东西。根据上面的两行代码可知,T就是_data的类型,而Ref是T&或const T&,所以知道为什么要写成模板了吧,你要是不写模板,就要实现两次关于T&以及const T&的代码,造成代码冗余。Ptr同理。

2.对于list这样对原生态指针进行封装(类模板)的,都是不可以直接使用*以及->的,都需要自己去自定义实现。这里的typedef也是为了避免代码的冗余。

3.*是对一个指针的解引用,拿到的是数据,所以说,用的是Ref(即T&或const T&),->跟*一样,都是访问元素的操作。只不过在标准迭代器设计中,->应该返回容器中元素的地址(指针)。

一个是元素的地址,一个是元素的引用,返回的都跟元素有关,所以说,是跟T(数据)有关。 

4.而++,--,这个是返回的是迭代器本身,因为`operator++`的作用是移动迭代器到下一个位置。

就是移动倒了下一个节点。所以返回`Self`或`Self&`确保了迭代器类型的正确性。如果返回`Ref`或`Ptr`,则会导致类型不匹配。

可以这么理解,就是从小到大,小就是节点中的数据的访问就是返回的是Ref以及Ptr,而大就是整个节点的移动。

5.判断两个节点是否相等,传参用的是self,因为判断节点时候相等,本来就是判断连那两个迭代器是否相等(节点中的元素),因为对元素的访问用的就是迭代器。

好了,这个类模板中的问题我已经说清楚了,接下来看最后一个类模板。

template<class T>
class list
{
    typedef list_node<T> Node;
public:
    typedef list_iterator<T, T&, T*> iterator;
    typedef list_iterator<const T, const T&, const T*> const_iterator;
    iterator begin()
    {
        return iterator(_head->_next);
    }
    iterator end()
    {
        return iterator(_head);//注意是_head,不是_head->_prev
    }
    const_iterator begin() const //后面的这个const 不能忘了
    {
        return const_iterator(_head->_next);
    }
    const_iterator end() const
    {
        return const_iterator(_head);//注意是_head,不是_head->_prev
    }
    /*~list()
    {
        _data = nullptr;
        _head->_next = _head->_prev = _head;
    }*/
    //你这个写的不是析构,顶多算是初始化,只有一个哨兵位。
    ~list()
    {
        clear();
        delete _head;
        _head = nullptr;//别忘了置为空
    }
    //拷贝构造
    list(List<T>& s)
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;

        _size = 0;
        for (auto& e : s)
        {
            push_back(e);
        }
    }
    //lt2=lt3
    list<T>& operator=(List<T> ta)
    {
        swap(ta);
        return *this;
    }
    void swap(List<int>& ta)
    {
        std::swap(_head, ta._head);
        std::swap(_size, ta._size);
    }
    list(initializer_list<T> il)//列表初始化的模拟实现
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;

        _size = 0;
        for (auto& e : il)
        {
            push_back(e);
        }
    }
    void clear()//clear的模拟实现
    {
        List<int>::iterator it = begin();
        while (it != end())
        {
            it=erase(it);
            it++;
        }
    }

    //当你写一个类的时候,一定要注意,这个类的构造尤其重要
        //就比如我这个,我如果说不写这个构造的话,那么编译器会默认构造,那么_head就是空
        //你是在end()位置插入,也就是_head位置,你的_head还是空,
        //当你的pos._node获得节点的时候,这个还是空呢,是访问不了任何成员的。
    list()    //因为下面要用到cur去访问,所以记住,空节点访问不了任何成员
    {
        _head = new Node;
        _head->_prev = _head;
        _head->_next = _head;
        _size = 0;
    }

    void insert(iterator pos,const T& x)
    {
        
        /*Node* cur = pos._node;*/
        /*newnode->_next = pos._node;
        newnode->_prev = _head;
        pos._node->_prev = newnode;
        _head->_next = newnode;*/
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(x);
        //prev newnode cur
        newnode->_next = cur;
        newnode->_prev = prev;
        prev->_next = newnode;
        cur->_prev = newnode;
        ++_size;
    }
    iterator erase(iterator pos)
    {
        assert(pos != end());
        Node* cur = pos._node;
        Node* next = cur->_next;//这儿有错
        Node* prev = cur->_prev;
        next->_prev = prev;
        prev->_next = next;
        delete cur;
        cur = nullptr;
        --_size;
        return next;
        //return iterator(next)
        //这样写也可以,第二种写法,这个类型也是iterator的,正好
        //第一种写法也可以,只不过单参数返回支持隐式类型转换,所以说也不要担心他的类型问题。
    }
    void push_back(const T& x)
    {
        insert(end(), x);
    }
    void push_front(const T& x)
    {
        insert(begin(), x);
    }
    void pop_back()
    {
        erase(--end());
    }
    void pop_front()
    {
        erase(begin());
    }
private:
    Node* _head;
    size_t _size;
};

1.为什么这里的erase返回的是next(下一个位置)?而vector中返回的是原位置?这里都涉及到迭代器失效的问题,不过呢,vector的底层是数组啊,底层是连续的,而且它的erase后,后面的元素都得往前移动一个,所以说vector的erase返回的还是下一个元素位置,只不过下一个元素的位置现在是被删除元素的位置。而list的erase后,所有的元素的位置并不发生任何变化,所以说这个返回的是真真正正的下一个元素的位置。

2.list迭代器失效:

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无 效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入 时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭 代器,其他迭代器不会受到影响。

3.这里有些博友可能会有些疑问:iterator begin()不是迭代器嘛?不是应该定义在iterator那个模板中嘛?为什么会出现在list这个大模板中?

首先,begin()和end()是容器的成员函数,属于容器的接口,用于获取容器的起始和结束迭代器。迭代器类本身不负责管理容器的元素范围,它只负责遍历和访问元素。因此,begin()的位置应该在容器类中,而不是迭代器类里。

其次:迭代器的作用是提供一种访问容器元素的方法,但它并不了解容器的内部结构,比如链表的头节点_head的位置。只有容器自己知道如何获取起始和结束的位置,所以容器必须提供这些方法。如果让迭代器自己生成begin(),它需要访问容器的私有成员(如_head),这会破坏封装性,或者需要将迭代器设为容器的友元,增加耦合。

总结下来就是list中实现的是整个迭代器,而iterator中实现的是迭代器对元素的访问,并不关心整个迭代器是怎么运行的,搭建的,它只关心怎么访问数据。其实这种设计符合面向对象的原则,容器管理数据,迭代器提供访问方式,各司其职。 

好,还剩下几个问题,把他们说清楚:

1.为什么前两个模板都是使用struct?使用class不可以吗?

首先看list_node结构体,它有三个成员变量:_next、_prev和_data,构造函数也是public的。如果用class的话,默认是private,需要显式声明public才能让外部访问这些成员。而用struct的话,构造函数和成员变量默认都是public的,这样在list类中可以直接访问这些成员,比如在list的insert或erase函数里,可以直接操作节点的_next和_prev指针。为了更方便的访问成员变量,且要频繁的访问成员变量,就将那个类置为struct。

struct用于那些需要所有成员公开的情况,比如节点和迭代器,而class用于需要封装的情况,比如容器本身。这样的选择主要是基于默认访问权限的不同,简化代码的同时保持必要的封装性。

2.这三个模板写一块不可以吗?为什么要分开写?

首先,我需要确认这三个模板各自的职责:

1. **list_node**:通常是一个结构体,包含数据和指向前后节点的指针,作为链表的节点。

2. **list_iterator**:迭代器类,用于遍历链表,提供操作符重载如++、--、*、->等。

3. **list**:链表类本身,管理节点的创建、删除,提供插入、删除等接口。

而之所以不把他们设置为一个类,是因为你的class中的成员变量不想被别人访问,但是你的其他的类中的成员变量确实被频繁访问,要是都放到一个类中,那肯定就混乱了。

其实就是每个模板负责不同的职责,分离关注点,符合良好的面向对象设计原则

4.最后一个问题

先来看一段代码:

struct AA

{

        int _a1;

        int _a2;

        AA(int a1 = 1, int a2 = 1)

                :_a1(a1)

                ,_a2(a2)

                {}

};

list<AA> lt1;

lt1.push_back({ 1,1 });

lt1.push_back({ 2,2 });

lt1.push_back({ 3,3 });

list<AA>::iterator lit1 = lt1.begin();

while (lit1 != lt1.end())

{

        //cout << (*lit1)._a1 <<":"<< (*lit1)._a2 << endl;

        // 特殊处理,省略了一个->,为了可读性

        cout << lit1->_a1 << ":" << lit1->_a2 << endl;

        cout << lit1.operator->()->_a1 << ":" << lit1.operator->()->_a2 << endl;

        ++lit1;

}

cout << endl;

1.好,咱们先来处理那个遗留下来的问题: 为什么pos后要用"."来访问成员变量,首先:这个地方iterator是一个类,不是指针,所以,pos是这个类实例化出的对象,所以现在知道这里为什么用“."来访问成员变量了吧。

2.这里,原先咱们说T是一个数据类型对吧,那么这里的AA就是_data,所以,_data中还有数据:_a1,_a2。所以,这里_data就是一个struct类。

2.1先来看注释掉的那个代码,其实是调用了operator*,返回的是AA&,而AA又是一个类,所以访问其中的元素就是用到了"."。

2.2

-> 用于指针:当变量是指向结构体/类类型的指针时,用 -> 访问成员(等价于 (*lit1)._a1)。

lit1.operator->()->_a1是类实例化出的lit1对象,先调用operator->()重载,返回的是AA*,而AA(_data)是个类,所以再次访问AA类中的成员变量需要用到_>。

其实这个等价于lit1._node->_data._a1。

且 lit1.operator->()->_a1简写就是lit1->_a1。

lit1->_a1:调用 list_iterator::operator->(),返回指向当前节点数据(AA 对象)的指针(AA*)。等价代码:AA* ptr = &(lit1._node->_data); // operator-> 返回 &_data cout << ptr->_a1 << ":" << ptr->_a2 << endl;

ok,那么关于这个模块的所有问题我已经讲清楚了,各位如果能看到最后,那么经过我的讲解,一定可以理解这些问题。

本篇完...................

 

 

 

 

相关文章:

C++STL——容器-list(含模拟实现,即底层原理)(含迭代器失效问题)(所有你不理解的问题,这里都有解答,最详细)

目录 1.迭代器的分类 2.list的使用 2.1 list的构造 2.2 list iterator 2.3 list capacity 2.4 list element access ​编辑 2.5 list modifiers ​编辑2.5.1 list插入和删除 2.5.2 insert /erase 2.5.3 resize/swap/clear ​编辑 2.6 list的一些其他接口…...

Linux系统编程之虚拟内存

概述 计算机内存是临时存储数据的地方&#xff0c;它比硬盘快得多&#xff0c;但容量有限。现代操作系统通过虚拟内存技术&#xff0c;使得每个进程都感觉自己独占整个地址空间&#xff0c;这不仅提高了安全性&#xff0c;也简化了内存管理。 物理内存&#xff1a;实际安装在计…...

笔试专题(八)

文章目录 平方数&#xff08;数学&#xff09;题解代码 DNA序列&#xff08;固定长度的滑动窗口&#xff09;题解代码 压缩字符串 &#xff08;双指针 模拟&#xff09;题解代码 chika和蜜柑 &#xff08;top k问题 排序 pair&#xff09;题解代码 平方数&#xff08;数学&a…...

Linux:基础IO---软硬链接动静态库前置知识

序&#xff1a;上一个章节&#xff0c;我从硬件出发&#xff0c;由宏观到微观&#xff0c;由具体到抽象&#xff0c;围绕研究对象未被打开的文件来讲解&#xff0c;操作系统是如何对一个大块的磁盘进行管理的&#xff0c;从而引进inode的概念&#xff0c;加深了对文件的理解&am…...

Arm CPU安全通告:基于TrustZone的Cortex-M系统面临多重故障注入攻击

安全之安全(security)博客目录导读 目录 一、概述 二、致谢 三、参考文献​​​​​​Black Hat USA 2022 | Briefings Schedule 四、版本历史 一、概述 Arm注意到BlackHat 2022大会官网发布的演讲摘要《糟糕..&#xff01;我又一次故障注入成功了&#xff01;——如何突…...

测试第二课-------自动化测试

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…...

深入探索Linux开发工具:Vim与Yum

目录 引言 Vim&#xff1a;强大的文本编辑利器 Vim的基本概念 Vim的基本操作 Vim正常模式命令集 Vim末行模式命令集 Vim的配置 使用插件拓展Vim功能 Yum&#xff1a;便捷的Linux软件包管理器 注意事项 结语 引言 在Linux的世界里&#xff0c;高效的开发工具是提升生…...

玩转ChatGPT:使用深入研究功能梳理思路

一、写在前面 前我尝试用ChatGPT的Deep Research&#xff08;深入研究&#xff09;功能来梳理文献&#xff0c;效果相当不错。最近&#xff0c;谷歌的Gemini 2.5 Pro也推出了类似功能&#xff0c;从网络测评来看&#xff0c;其表现与ChatGPT不相上下&#xff0c;而且还可以免费…...

UE5蓝图实现打开和关闭界面、退出

Button_Back 和Button_Exit是创建的两个按钮事件。 1.Create Widget 创建界面&#xff08;打开界面&#xff09; 2.Add to Viewport 添加到视图 3.remove form Parent&#xff0c;Target&#xff1a;self 从父节点移除当前界面&#xff08;关闭界面&#xff09; 4.Quit Game 退…...

实现vlan间的通信

这是第一种方法&#xff08;更推荐第三种&#xff09; PC1划分为vlan10&#xff0c;PC2划分为vlan20&#xff0c;实现PC1和PC2之间通信很简单&#xff0c;我们只需将网关都设置好&#xff0c;将交换机的0/0/1、0/0/3设置成vlan10&#xff0c;0/0/2、0/0/4设置成vlan20&#xf…...

Linux上位机开发实践(opencv算法硬件加速)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 图像处理里面&#xff0c;opencv基本是一个标准模块。但是由于图像处理的特点&#xff0c;如果所有的算法都是cpu来做的话&#xff0c;效率会很低。…...

智慧社区数据可视化中枢平台——Axure全场景交互式大屏解决方案

在数字化治理的时代浪潮中&#xff0c;社区管理正面临数据碎片化、响应滞后、决策盲区等核心挑战。如何将分散的安防、环境、能源、民生服务等数据整合为可操作的智慧洞察&#xff1f;如何让冰冷的数字转化为社区管理者手中的决策利器&#xff1f;Axure智慧社区可视化大屏原型模…...

动态路由, RIP路由协议,RIPv1,RIPv2

动态路由 1、回顾 路由&#xff1a;从源主机到目标主机的过程 源主机发送数据给目标主机&#xff0c;源主机会查看自身的路由信息 如果目标主机是自己同网段&#xff0c;源主机查看的是直连路由 如果目标主机和自己不同网段&#xff0c;源主机查看的是静态路由、动态路由、默…...

C++:STL的常用容器(string/vector/deque/stack/queue/list/set/multiset/map/multimap)

程序员Amin &#x1f648;作者简介&#xff1a;练习时长两年半&#xff0c;全栈up主 &#x1f649;个人主页&#xff1a;程序员Amin &#x1f64a; P   S : 点赞是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全…...

【unity游戏开发入门到精通——UGUI】Canvas画布组件

注意&#xff1a;考虑到UGUI的内容比较多&#xff0c;我将UGUI的内容分开&#xff0c;并全部整合放在【unity游戏开发——UGUI】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 一、Canvas画布组件1、Canvas组件用来干啥2、场景中可以有多个Canvas对象 二、…...

MyBatis 中 Mapper 传递参数的多种方法

# MyBatis Mapper 传递参数的多种方法及其优势 在使用 MyBatis 进行数据库操作时&#xff0c;Mapper 接口的参数传递是一个非常基础但又十分重要的部分。不同的参数传递方式适用于不同的场景&#xff0c;合理选择可以大大提高代码的可读性和维护性。本文将详细介绍几种常见的 …...

学习海康VisionMaster之平行线计算

一&#xff1a;进一步学习了 今天学习下VisionMaster中的平行线计算&#xff0c;这个是拟合直线的扩展应用&#xff0c;针对需要计算平行线的应用场合&#xff0c;可以方便的生成对应的另外一条平行线 二&#xff1a;开始学习 1&#xff1a;什么是平行线计算&#xff1f; 如果…...

MyBatis Mapper 传递参数的多种方法

1. 使用顺序传参法(不推荐) 方法描述 直接通过位置来引用参数,例如 ( arg0 arg1 … ) 或者 (param1, param2…)。 示例代码 List<User> selectUsers(String name, Integer age); <...

探索 Vue 3 响应式系统:原理与实践

Vue 3 响应式系统凭借 Proxy 的优势&#xff0c;提供更强大、灵活的响应式方案。理解其原理与 API&#xff0c;能写出更高效、可维护的 Vue 应用。不断探索其细节&#xff0c;是进阶 Vue 开发的关键。 探索 Vue 3 响应式系统&#xff1a;原理与实践 Vue 3 的响应式系统是其核…...

【LeetCode 热题100】二叉树构造题精讲:前序 + 中序建树 有序数组构造 BST(力扣105 / 108)(Go语言版)

&#x1f331; 二叉树构造题精讲&#xff1a;前序 中序建树 & 有序数组构造 BST 本文围绕二叉树的两类构造类题目展开解析&#xff1a; 从前序与中序遍历序列构造二叉树 将有序数组转换为二叉搜索树 我们将从「已知遍历构造树」和「平衡构造 BST」两个角度&#xff0c;拆…...

开源语音文本自动对齐模型:Llama-OuteTTS-1.0-1B

OuteTTS 1.0 介绍与使用指南 1. 重要采样考虑 重复惩罚机制&#xff1a;OuteTTS 1.0 要求对最近的64个token应用重复惩罚&#xff0c;而不是对整个上下文窗口。对整个上下文窗口进行惩罚会导致输出质量下降。推荐工具&#xff1a;llama.cpp 和 EXL2 提供了可靠的输出质量&…...

基于SpringBoot的电影订票系统(源码+数据库+万字文档+ppt)

504基于SpringBoot的电影订票系统&#xff0c;系统包含两种角色&#xff1a;管理员、用户主要功能如下。 【用户功能】 首页&#xff1a;浏览系统电影动态。 资讯信息&#xff1a;获取有关电影行业的新闻和资讯。 电影信息&#xff1a;查看电影的详细信息和排片情况。 公告信…...

基于SpringBoot汽车零件商城系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...

Python数据可视化:从脚本到海报级图表

Python数据可视化:从脚本到海报级图表 引言 在数据分析和科学计算领域,Python 是一种强大且灵活的工具。本文将带您了解如何使用 Python 进行数据可视化,从简单的脚本到生成高质量的海报级图表。我们将重点介绍如何使用 Matplotlib 库来创建、保存和优化图表,以便在各种场…...

使用Java截取MP4文件图片的技术指南

在多媒体处理中&#xff0c;从视频文件中截取图片是一个常见的需求。本文将详细介绍如何使用Java结合FFmpeg实现从MP4文件中截取图片的功能。我们将通过几种不同的方法来实现这一目标&#xff0c;包括直接调用FFmpeg命令行工具、使用JavaCV库以及使用JAVE库。 环境准备 在开始…...

C++(初阶)(十一)——list

十一&#xff0c;list 带头循环双向链表。 遍历方式&#xff1a;迭代器&#xff0c;不再支持operate[]&#xff0c;operate[]适用于底层是数组的结构。 remove删除值&#xff0c;如果有多个相同的值&#xff0c;都会删除。 接口介绍 下面会介绍list的一些接口 构造 构造…...

leetcode 139. Word Break

这道题用动态规划解决。 class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {unordered_set<string> wordSet;for(string& word:wordDict){wordSet.insert(word);}int s_len s.size();//s的下标从1开始起算&#xff0c;dp[j]…...

5.1、深度剖析 docker run 命令:原理阐释与数据持久化实践探究

5.1、深度剖析 docker run 命令:原理阐释与数据持久化实践探究 1、更换国内yum源2、更换国内docker源3、卸载旧版docker4、docker安装5、镜像加速器6、镜像下载7、docker run命令交互式启动-it非交互式后台运行其他参数8、持久化存储目录挂载数据卷挂载数据同步1、更换国内yum…...

【AI大模型】大模型RAG技术Langchain4j 核心组件深入详解

目录 一、前言 二、Langchain4j概述 2.1 Langchain4j 是什么 2.2 Langchain4j 主要特点 2.3 Langchain4j 核心组件 2.4 Langchain4j 核心优势 三、Langchanin4j组件应用实战 3.1 前置准备 3.1.1 导入如下依赖 3.1.2 获取apikey 3.1.3 获取官方文档 3.2 聊天组件 3.…...

【Flink运行时架构】重要概念

前面我们讲了Flink运行时的核心组件和提交流程&#xff0c;但有些细节需要进一步的思考&#xff0c;一个具体的作业是怎样从编写的代码转换成TaskManager可以执行的任务的呢&#xff1f;JobManager在收到提交的作业之后&#xff0c;又是如何确定总共有多少任务、需要配置多少资…...

oracle命令上下左右键无法使用如何解决?

1、问题如图 2、解决办法 (1) 安装readline yum -y install readline* &#xff08;2&#xff09;安装 rlwrap ##下载 wget http://files.cnblogs.com/files/killkill/rlwrap-0.30.tar.gz.zip ##解压 tar -xzvf rlwrap-0.30.tar.gz.zip ##编译安装 ./configure make &&…...

[文献阅读] chinese-roberta Pre-Training With Whole Word Masking for Chinese BERT

文献信息&#xff1a;Pre-Training With Whole Word Masking for Chinese BERT | IEEE Journals & Magazine | IEEE Xplore 哈工大和科大讯飞联合发表的用于中文NLP任务的基于BERT的改进模型&#xff0c;在中文NLP任务取得了最先进的性能。 摘要 原本的BERT使用随机掩蔽的…...

QML ListView 与 C++ 模型交互

在 Qt 中&#xff0c;QML 的 ListView 可以与 C 模型进行交互&#xff0c;这是实现复杂数据展示和业务逻辑的常见方式。以下是几种主要的交互方法&#xff1a; 1. 使用 QAbstractItemModel 派生类 这是最强大和灵活的方式&#xff0c;适合复杂数据结构。 C 端实现 cpp // …...

使用SSH解决在IDEA中Push出现403的问题

错误截图&#xff1a; 控制台日志&#xff1a; 12:15:34.649: [xxx] git -c core.quotepathfalse -c log.showSignaturefalse push --progress --porcelain master refs/heads/master:master fatal: unable to access https://github.com/xxx.git/: The requested URL return…...

MacOs下解决远程终端内容复制并到本地粘贴板

常常需要在服务器上捣鼓东西&#xff0c;同时需要将内容复制到本地的需求。 1-内容是在远程终端用vim打开&#xff0c;如何用vim的类似指令达到快速复制到本地呢&#xff1f; 假设待复制的内容&#xff1a; #include <iostream> #include <cstring> using names…...

修改idea/android studio等编辑器快捷注释从当前行开头的反人类行为

不知道什么时候开始&#xff0c;idea编辑的快捷注释开始从当前行开头出现了&#xff0c;显得实在是难受&#xff0c;我只想让在当前行代码的部份开始缩进两个字符开始&#xff0c;这样才会显得更舒服。不知道有没有强迫症的猴子和我一样&#xff0c;就像下面的效果&#xff1a;…...

密码加密方式

密码加密方式全面解析 密码安全是系统安全的第一道防线&#xff0c;以下是主流的密码加密技术分类和实现方式&#xff1a; 一、基础加密方式 1. 对称加密 特点&#xff1a;加密解密使用相同密钥 AES (Advanced Encryption Standard) 密钥长度&#xff1a;128/192/256位示例…...

Python10天突击--Day 2: 实现观察者模式

以下是 Python 实现观察者模式的完整方案&#xff0c;包含同步/异步支持、类型注解、线程安全等特性&#xff1a; 1. 经典观察者模式实现 from abc import ABC, abstractmethod from typing import List, Anyclass Observer(ABC):"""观察者抽象基类""…...

【C#】.NET 8适配器模式实战:用C#实现高可用系统集成与接口桥接艺术

系统集成挑战与适配器模式的价值 当需要整合不同架构或API的系统时&#xff0c;接口兼容性问题往往成为拦路虎。**适配器设计模式&#xff08;Adapter Pattern&#xff09;**通过转换接口形态&#xff0c;完美解决这种不兼容性问题。本文将通过C# .NET 8实战演示适配器模式的基…...

方案精读:51页 财政数据信息资源目录数据标准存储及大数据资产化规划方案【附全文阅读】

该方案聚焦财政数据信息资源管理,适用于财政部门工作人员、数据管理与分析人员以及关注财政大数据应用的相关人士。 方案旨在构建财政数据资源目录,推动大数据在财政领域的应用与落地。整体规划上,以 “金财工程” 应用支撑平台为基础,建立省、市、县三级目录体系,遵循相关…...

【CVE-2024-7881】ARM CPU漏洞安全通告

安全之安全(security)博客目录导读 目录 一、概述 二、CVE详情 三、受影响产品 四、修复建议 五、致谢 六、版本历史 一、概述 基于Arm架构的部分CPU中发现一个安全问题&#xff1a;非特权上下文可能触发数据内存依赖型预取引擎&#xff08;data memory-dependent pref…...

idea中提高编译速度研究

探索过程&#xff1a; 有三种情况&#xff1a; 第一种&#xff1a; idea中用eclipse编译器编译springboot项目&#xff0c;然后debug启动Application报错找不到类。 有待继续研究。 第二种&#xff1a; idea中用javac编译器编译springboot项目&#xff0c;重新构建用时&a…...

基于Yocto构建Ubuntu 24.04 ARM64 Qt工具链

以下是基于Yocto构建Ubuntu 24.04 ARM64 Qt工具链的完整方案&#xff0c;综合多篇技术文档整理而成&#xff1a; 一、系统环境准备 Ubuntu基础系统‌ 建议选择Ubuntu 24.04 LTS服务器版或桌面版&#xff0c;需满足至少300GB磁盘空间和16GB内存‌ 若使用ARM64架构主机可直接运…...

如何使用阿里云邮件推送免费群发邮件

最近一直想利用自己的阿里云账号开一个邮件推送服务&#xff0c;同时还可以用python来实现邮件群发&#xff0c;之前没有成功&#xff0c;今天又尝试了一次终于成功了&#xff0c;现将过程记录如下&#xff0c;也便于网友们少走弯路。 一、申请阿里云账号 阿里云注册可以用淘…...

利用 Genspark 和 AI IDE 一键配置 Java 开发环境

以下是以 CSDN 博客风格撰写的文章&#xff0c;基于你提到的“利用 Genspark 和 AI IDE 实现 Java 环境一键配置”的流程。文章结构清晰&#xff0c;内容详实&#xff0c;符合 CSDN 技术博客的常见风格&#xff0c;包含标题、简介、目录、正文、代码示例和总结。 利用 Genspark…...

【软考系统架构设计师】计算机网络知识点

1、 TCP/IP协议族 2、 数据链路层 解决三个基本问题&#xff1a; 封装成帧&#xff08;在⼀段数据的前后分别添加首部和尾部&#xff09; 透明传输&#xff08;发送⽅&#xff1a;若数据部分出现帧开始符或者帧结束符&#xff0c;会在其前面加转义字符&#xff1b;接收⽅&…...

RFID技术概览

一、RFID技术定义 RFID&#xff08;Radio Frequency Identification&#xff0c;射频识别&#xff09; 是一种通过无线电信号识别目标对象并获取相关数据的非接触式自动识别技术。它利用射频信号的空间耦合&#xff08;电感或电磁耦合&#xff09;实现无物理接触的信息传递与目…...

中间件--ClickHouse-2--OLAP和OLTP

OLAP&#xff08;Online Analytical Processing&#xff0c;联机分析处理&#xff09;和OLTP&#xff08;Online Transaction Processing&#xff0c;联机事务处理&#xff09;是两种不同类型的数据处理系统&#xff0c;它们分别针对不同的应用场景和需求。 1、OLTP&#xff0…...

使用ADB工具分析Android应用崩溃原因:以闪动校园为例

使用adb工具分析模拟器或手机里app出错原因以闪动校园为例 使用ADB工具分析Android应用崩溃原因&#xff1a;以闪动校园为例 前言 应用崩溃是移动开发中常见的问题&#xff0c;尤其在复杂的Android生态系统中&#xff0c;找出崩溃原因可能十分棘手。本文将以流行的校园应用&q…...

C++双链表介绍及实现

双链表详解 1. 基本概念 双链表&#xff08;双向链表&#xff09;​ 是一种链式数据结构&#xff0c;每个节点包含两个指针&#xff1a; ​前驱指针&#xff08;pre&#xff09;​&#xff1a;指向直接前驱节点​后继指针&#xff08;next&#xff09;​&#xff1a;指向直接…...