C++:List的使用和模拟实现

news2025/3/18 15:46:35

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

文章目录

目录

文章目录

前言

一 list的介绍及使用

1.1 list的介绍

1.2 list的使用 

1.2.1 list的构造

1.2.2 list iterator的使用

1.2.3 list capacity 

1.2.4 list element access 

 1.2.5 list modifiers

1.2.6 list的迭代器失效 

 1.2.7 List中sort的效率测试

二、list的模拟实现

 2.1 正向迭代器的实现

2.1.1 正向迭代器的封装 

 2.1.2 迭代器的使用

2.2 list相关的成员函数

 2.2.1 构造函数

2.2.1.1 默认构造函数

 2.2.1.2 有参构造函数

 2.2.1.3 迭代器区间构造函数 

2.2.1.4 拷贝构造 

1.传统 

2.现代 

2.2.2 析构函数和clear

2.2.2.1 析构函数

2.2.2.2 clear()

 2.2.3 赋值重载

2.2.4.1 empty、size

 2.2.4.2 insert

2.2.4.3 erase 

2.2.4.4 尾插尾删头插头删 

2.3 反向迭代器的实现 

 

三 list模拟实现的全部代码


前言

本篇详细介绍了list的使用和模拟实现,让使用者了解list,而不是仅仅停留在表面,更好的模拟,为了更好的使用. 文章可能出现错误,如有请在评论区指正,让我们一起交流,共同进步!


一 list的介绍及使用

1.1 list的介绍

list文本介绍

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

1.2 list的使用 

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展 的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置

【注意】

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动 

1.2.3 list capacity 

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

1.2.4 list element access 

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

 1.2.5 list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

list中还有一些操作,需要用到时大家可参阅list的文档说明。

1.2.6 list的迭代器失效 

前面说过,此处大家可将迭代器暂时理解成类似于指针

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。

因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1()
 {
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array+sizeof(array)/sizeof(array[0]));
 
    auto it = l.begin();
    while (it != l.end())
    {
        // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给
其赋值
        l.erase(it);  
        ++it;
    }
 }

 // 改正
void TestListIterator()
 {
     int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
     list<int> l(array, array+sizeof(array)/sizeof(array[0]));
     auto it = l.begin();
     while (it != l.end())
    {
         l.erase(it++);    
    }
 }

 1.2.7 List中sort的效率测试

我们用一段代码来测试一下list中sort的性能

#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
void test_op()
{
	srand((unsigned int)time(NULL));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	list<int> lt1;
	list<int> lt2;
	for (int i = 0; i < N; ++i)
	{
		int e = rand();
		lt1.push_back(e);
		lt2.push_back(e);
	}
	// 拷贝到vector排序,排完以后再拷贝回来
	int begin1 = clock();
	for (auto e : lt1)
	{
		v.push_back(e);
	}
	sort(v.begin(), v.end());
	size_t i = 0;
	for (auto& e : lt1)
	{
		e = v[i++];
	}
	int end1 = clock();
	//list调用自己的sort
	int begin2 = clock();
	lt2.sort();
	int end2 = clock();
	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

 

会发现哪怕我先拷贝到vector排完再拷贝回去效率都比list的sort效率高,所以list的sort实际中意义不是很大!! 

二、list的模拟实现

 2.1 正向迭代器的实现

2.1.1 正向迭代器的封装 

我们在学习vector的时候,发现vector的迭代器就是一个原生指针T*,这得益于vector的空间的连续性

那我们还能像vector一样用原生指针去修饰迭代器吗?

不能,链表空间上是不连续的,那我们对一个节点指针进行加减,就很难说能不能找到下一个节点,更多的是找不到的情况

我们在数据结构中访问下一个节点,往往是利用当前节点存的下一节点的地址来进行访问的

所以我们可以将迭代器单独封装成一个类去管理节点

template<class T, class Ref, class Ptr> //Ref == T& Ptr == T*
struct ListIterator //这里使用struct是因为我们要多次访问成员,也可使用class+public
{
    typedef ListNode<T>* PNode;
    typedef ListIterator<T, Ref, Ptr> Self;
    ListIterator(PNode pNode = nullptr) 
        :_pNode(pNode)
    {}
    ListIterator(const Self& l) 
        :_pNode(l._pNode)
    {}
    T& operator*() //解引用
    {
        return _pNode->_val;
    }
    T* operator->() 
    {
        return &_pNode->_val;
    }
    Self& operator++() //前置++
    {
        _pNode = _pNode->_pNext;
        return *this;
    }
    Self operator++(int) //后置++
    {
        Self temp(*this);
        _pNode = _pNode->_pNext;
        return temp;
    }
    Self& operator--() //前置--
    {
        _pNode = _pNode->_pPre;
        return *this;
    }
    Self& operator--(int) //后置--
    {
        Self temp(*this);
        _pNode = _pNode->_pPre;
        return temp;
    }
    bool operator!=(const Self& l) //不相等判断
    {
        return _pNode != l._pNode;
    }
    bool operator==(const Self& l) //相等判断
    {
        return !(*this != l);
    }
    PNode _pNode;
};

 T* operator->()  大家可能有疑惑

 当我们的节点里存的是内置类型或者STL容器是,库里面的<<重载了这些来读取数据

但如果是我们自己写的类型(如class CH),<<并不能读取该类型的数据

因此我们要取来节点自定义类的指针,来访问该类的内部的数据(因为最底层都是内置类型

 2.1.2 迭代器的使用

 template<class T>
 class list
 {
     typedef ListNode<T> Node;
     typedef Node* PNode;
 public:
     typedef ListIterator<T, T&, T*> iterator;
     typedef ListIterator<T, const T&, const T&> const_iterator;
 public:
    iterator begin()
    {
        return iterator(_pHead->_pNext);
    }
    iterator end()
    {
        return iterator(_pHead);
    }
    const_iterator begin()const
    {
        return const_iterator(_pHead->_pNext);
    }
    const_iterator end()const
    {
        return const_iterator(_pHead);
    }
private:
    PNode _pHead;    
}

这边我们用到了匿名对象。 

这里的const迭代器为什么不能直接用const修饰普通迭代器??

 因为typedef碰到const的话,就不是简单的字符串替换  实际上你以为的const T* ,在这里变成了T*const ,因为迭代器我们是希望他可以进行++和--的,而我们只是不希望他指向的内容给改变,所以我们的const要修饰的是指针的内容,而不是修饰指针。

2.2 list相关的成员函数

 2.2.1 构造函数

2.2.1.1 默认构造函数
list()
{
    _pHead = new Node;
    _pHead->_pPre = _pHead;
    _pHead->_pNext = _pHead;
}    

因为无论如何都要有哨兵节点(方便我们不用判空,所以我们直接封装一个 

void CreateHead()
{
    _pHead = new Node;
    _pHead->_pPre = _pHead;
    _pHead->_pNext = _pHead;
}
 2.2.1.2 有参构造函数
 list(int n, const T& value = T())
 {
     CreateHead();
     for (int i = 0; i < n; ++i)
         push_back(value);
 }
 2.2.1.3 迭代器区间构造函数 
template <class Iterator>
list(Iterator first, Iterator last)
{
    CreateHead();
    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}
2.2.1.4 拷贝构造 
1.传统 
//拷贝构造函数传统写法
list(const list<T>& l)
{
	CreateHead();
	for (auto e : l)
		push_back(e);
}
2.现代 
void swap(list<T>& l)
{
    std::swap(_pHead, l._pHead);
}

list(const list<T>& l)
{
    CreateHead();
    // 用l中的元素构造临时的temp,然后与当前对象交换
    list<T> temp(l.begin(), l.end());
    swap(temp);
}

2.2.2 析构函数和clear

2.2.2.1 析构函数
~list()
{
    clear();
    delete _pHead;
    _pHead = nullptr;
}
2.2.2.2 clear()
void clear()
{
    iterator p = begin();
    while (p != end())
    {
        p = erase(p);
    }

    _pHead->_pPre = _pHead;
    _pHead->_pNext = _pHead;
}

 2.2.3 赋值重载

list<T>& operator=(const list<T> l)
{
    swap(l);
    return *this;
}

2.2.4 修改相关函数(Modifiers)

2.2.4.1 empty、size
size_t size()const
{
    size_t size = 0;
    ListNode* p = _pHead->_pNext;
    while (p != _pHead)
    {
        size++;
        p = p->_pNext;
    }
    return size;
}
bool empty()const
{
    return size() == 0;
}
 2.2.4.2 insert
// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    PNode newnode = new Node(val);
    PNode cur = pos._pNode;
    PNode prev = cur->_pPre;

    newnode->_pPre = prev;
    newnode->_pNext = cur;
    prev->_pNext = newnode;
    cur->_pPre = newnode;
    return iterator(newnode);
}
2.2.4.3 erase 
// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
    assert(pos != end());

    PNode prev = pos._pNode->_pPre;
    PNode next = pos._pNode->_pNext;
    prev->_pNext = next;
    next->_pPre = prev;
    delete pos._pNode;
    return iterator(next);//利用匿名对象返回
}
2.2.4.4 尾插尾删头插头删 
 // List Modify
 void push_back(const T& val)
 {
     insert(end(), val);
 }
 void pop_back()
 {
     erase(--end());
 }
 void push_front(const T& val)
 {
     insert(begin(), val);
 }
 void pop_front()
 {
     erase(begin());
 }

2.3 反向迭代器的实现 

 sgi版本下的反向迭代器,其实就是将构建一个反向迭代器的类将正向迭代器封装起来,这个时候正向迭代器的++就是反向迭代器的--

namespace bit
{
	// 适配器 -- 复用
	template<class Iterator, class Ref, class Ptr>   //Ref == T&  Ptr == T*
	struct Reverse_iterator
	{
		typedef Reverse_iterator<Iterator, Ref, Ptr> Self;
		Reverse_iterator(Iterator it)
			:_it(it)
		{}

		Ref operator*()
		{
			Iterator temp = _it;
			--temp;
			return *temp;
		}

		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self operator++(int)
		{
			Iterator tmp = _it;
			--_it;
			return tmp;
		}
		Self& operator--()
		{
			++_it;
			return *this;
		}

		Self operator--(int)
		{
			iterator temp = _it;
			++_it;
			return temp;
		}
		bool operator!=(const Self& s)
		{
			return _it != s._it;
		}

		bool operator==(const Self& s)
		{
			return _it == s._it;
		}


		Iterator _it;
	};
}

 为什么解引用的是前一个位置的元素???

 

 from C++:List的使用和模拟实现-CSDN博客 图来源

 

三 list模拟实现的全部代码

#pragma once
namespace ch
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T()) 
            :_pPre(nullptr)
            ,_pNext(nullptr)
            ,_val(val)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

    //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
        ListIterator(PNode pNode = nullptr) 
            :_pNode(pNode)
        {}
        ListIterator(const Self& l) 
            :_pNode(l._pNode)
        {}
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &_pNode->_val;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
        PNode _pNode;
    };

    template<class Iterator, class Ref, class Ptr>   //Ref == T&  Ptr == T*
    struct Reverse_iterator
    {
        typedef Reverse_iterator<Iterator, Ref, Ptr> Self;
        Reverse_iterator(Iterator it)
            :_it(it)
        {}

        Ref operator*()
        {
            Iterator temp = _it;
            --temp;
            return *temp;
        }

        Self& operator++()
        {
            --_it;
            return *this;
        }

        Self operator++(int)
        {
            Iterator tmp = _it;
            --_it;
            return tmp;
        }
        Self& operator--()
        {
            ++_it;
            return *this;
        }

        Self operator--(int)
        {
            iterator temp = _it;
            ++_it;
            return temp;
        }
        bool operator!=(const Self& s)
        {
            return _it != s._it;
        }

        bool operator==(const Self& s)
        {
            return _it == s._it;
        }

        Iterator _it;
    };

    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        //正向迭代器
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
        //反向迭代器
        typedef Reverse_iterator<iterator, T&, T*>  reverse_iterator;
        typedef Reverse_iterator<iterator, const T&, const T*>  const_reverse_iterator;
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
                push_back(value);
        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        list(const list<T>& l)
        {
            CreateHead();
            // 用l中的元素构造临时的temp,然后与当前对象交换
            list<T> temp(l.begin(), l.end());
            swap(temp);
        }
        list<T>& operator=(const list<T> l)
        {
            swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }

        ///
        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin()const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator end()const
        {
            return const_iterator(_pHead);
        }

        //反向迭代器(可读可写)
        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }

        reverse_iterator rend()
        {
            return reverse_iterator(begin());
        }
        //反向迭代器(可读不可写)
        const_reverse_iterator rbegin() const
        {
            return const_reverse_iterator(end());
        }

        const_reverse_iterator rend() const
        {
            return const_reverse_iterator(begin());
        }

        ///
        // List Capacity
        size_t size()const
        {
            size_t size = 0;
            ListNode* p = _pHead->_pNext;
            while (p != _pHead)
            {
                size++;
                p = p->_pNext;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }

        
        // List Access
        T& front()
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }

        
        // List Modify
        void push_back(const T& val)
        {
            insert(end(), val);
        }
        void pop_back()
        {
            erase(--end());
        }
        void push_front(const T& val)
        {
            insert(begin(), val);
        }
        void pop_front()
        {
            erase(begin());
        }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode newnode = new Node(val);
            PNode cur = pos._pNode;
            PNode prev = cur->_pPre;

            newnode->_pPre = prev;
            newnode->_pNext = cur;
            prev->_pNext = newnode;
            cur->_pPre = newnode;
            return iterator(newnode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(pos != end());

            PNode prev = pos._pNode->_pPre;
            PNode next = pos._pNode->_pNext;
            prev->_pNext = next;
            next->_pPre = prev;
            delete pos._pNode;
            return iterator(next);//利用匿名对象返回
        }
        void clear()
        {
            iterator p = begin();
            while (p != end())
            {
                p = erase(p);
            }

            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        PNode _pHead;
    };
}

总结

✨✨✨各位读友,本篇分享到内容是否更好的让你理解了C++的list,如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法】前缀和算法——和为k的子数组之和

题解&#xff1a;和为k的子数组之和(前缀和算法) 目录 1.题目2.题解思路2.1前缀和 哈希表&#xff0c;算法步骤&#xff1a;2.2细节如下&#xff1a;2.3参考代码&#xff1a; 3.总结及思考 1.题目 题目链接&#xff1a;LINK 2.题解思路 暴力求解自然不用多说&#xff0c;时…

七大经典排序算法——冒泡排序

文章目录 &#x1f4d1;冒泡排序介绍&#x1f324;️代码实现&#x1f324;️做个简单的优化&#x1f324;️复杂度和稳定性分析☁️结语 &#x1f4d1;冒泡排序介绍 冒泡排序是一种简单但效率较低的排序算法。它重复地比较相邻的两个元素&#xff0c;如果顺序不对则交换它们&…

刷题之路径总和Ⅲ(leetcode)

路径总和Ⅲ 这题和和《为K的数组》思路一致&#xff0c;也是用前缀表。 代码调试过&#xff0c;所以还加一部分用前序遍历数组和中序遍历数组构造二叉树的代码。 #include<vector> #include<unordered_map> #include<iostream> using namespace std; //Def…

著名书法家王杰宝做客央视频《笔墨写人生》艺坛人物经典访谈节目

印象网北京讯&#xff08;张春兄、冯爱云&#xff09;展示艺术风采&#xff0c;构建时代精神。5月25日&#xff0c;著名书法家、羲之文化传承人王杰宝&#xff0c;做客央视频《笔墨写人生》艺坛人物经典访谈节目&#xff0c;与中央电视台纪录频道主持人姚文倩一起&#xff0c;分…

web前端框架设计第十课-组件

web前端框架设计第十课-组件 一.预习笔记 组件&#xff1a;Vue最强大的功能之一 1.局部组件注册 注意事项&#xff1a;template标签中只能有一个根元素 2.全局组件的注册 注意事项&#xff1a;组件名的大小写需要注意&#xff08;实践&#xff09; 3.案例&#xff08;查询框…

解决 Failed to parse remote port from server output【Remote-SSH】【VSCode】

描述 一早起来&#xff0c;发现remote-ssh无法进入服务器容器&#xff0c;本地使用git bash进行ssh可正常连接服务器&#xff0c;基本确定是vscode工具本身的问题。重装本地用户的.vscode相关目录清空&#xff0c;vscode重装均无果&#xff0c;不建议尝试。弹窗信息为Could no…

SpringBoot高级原理详解

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

【ARM 裸机】按键输入

本节学习按键输入&#xff0c;先拷贝上一节工程文件&#xff0c; 1、驱动编写 新建 key 的 .h 和 .c 文件&#xff1b; 再查看一下硬件原理图如下&#xff1b; 由此可知&#xff0c;KEY0 按键接在 UART1_CTS 引脚上&#xff0c;默认情况下为高电平&#xff0c;按键按下为…

AI大模型探索之路-基础篇5:GLM-4解锁国产大模型的全能智慧与创新应用

目录 前言一、GLM4大模型总体概述二、GLM4和GPT4功能对比三、GLM4和GPT4性能对比1、基础能力&#xff08;英文&#xff09;2、指令跟随能力3、对齐能力4、长文本能力5、多模态-文生图 四、GLM-4 ALL Tools1、文生图2、代码解释器3、网页浏览4、Function Call5、多工具自动调用 …

CHI dataless 传输——CHI(4)

上篇介绍了read的操作类型&#xff0c;本篇我们来介绍一下dataless 目录 一、dataless操作概览 二、Non-CMO (Non-Cache Maintenance Operation) 1、CleanUnique 2、StashOnce and StashOnceSep 3、Evict 三、CMO (Cache Maintenance Operation) 一、dataless操作概览 名…

洛谷P3574 [POI2014] FAR-FarmCraft(树形dp)

洛谷 P 3574 [ P O I 2014 ] F A R − F a r m C r a f t &#xff08;树形 d p &#xff09; \Huge{洛谷P3574 [POI2014] FAR-FarmCraft&#xff08;树形dp&#xff09;} 洛谷P3574[POI2014]FAR−FarmCraft&#xff08;树形dp&#xff09; 文章目录 题意题目说明 思路标程 题目…

使用git生成SSH公钥,并设置SSH公钥

1、在git命令行里输入以下命令 ssh-keygen -t rsa 2、按回车&#xff0c;然后会看到以下字眼 Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/xxx/.ssh/id_rsa) 例&#xff1a; 3、继续回车&#xff0c;然后会看到以下字眼 Enter…

等保三级云防火墙正版--免费部署满足要求

正版授权内部部署配置授权免费 1、超时退出 2、病毒防护 3、防火墙策略 4、密码复杂度和登录失败处理 5、特征库 点赞关注 私信获取 获取授权 Q 8-5-0-3-4-7-3-3-5

Python--面向对象

面向对象⭐⭐ 1. 面向对象和面向过程思想 面向对象和面向过程都是一种编程思想,就是解决问题的思路 面向过程&#xff1a;POP(Procedure Oriented Programming)面向过程语言代表是c语言面向对象&#xff1a;OOP(Object Oriented Programming)常见的面向对象语言包括:java c g…

C++初阶之模板进阶

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.非类型模板参数 二.模板的特化 2.1引入 2.2全特化 2.3…

盖雅技能发展云,助力制造企业人效合一

制造行业尽管经历多次变革&#xff0c;但企业对人的管理始终是一项高度依赖经验和耗费人力的工作。随着供应链管理和生产设备的自动化、数字化升级&#xff0c;如何将第一生产要素——人&#xff0c;通过数字化的工具融入制造过程的闭环&#xff0c;对企业实现自动化工厂和智能…

【一个糟糕的词:省流】

今日思考&#xff0c;博主分享&#x1f4dd;&#xff0c;原文如下&#xff0c; 我最近听到了一个特别糟糕的词叫省流。我甚至认为这个词可以用来衡量一个人的智商啊&#xff0c;我们可以把一个知识简单的分成三部分问题&#xff0c;答案思维方式就是这个答案是怎么推导出来的啊…

解决Vue项目部署到服务器之后前端向后端发送请求报错404的问题(centos使用docker实现nginx的反向代理)

场景重现&#xff1a; 由于现在流行前后端分离的部署方式&#xff0c;但是按照正确方法部署&#xff08;如何部署可参考&#xff1a;&#xff09;之后&#xff0c;发现明明前后端都部署好了并且运行成功&#xff0c;但是前端发送的请求都是404。明明在vue项目中配置了跨域的相…

信息系统项目管理师十大管理计划内容概览

目录 1.项目章程2.项目管理计划3.范围管理计划4.需求管理计划5.进度管理计划6.成本管理计划7.质量管理计划8.资源管理计划9.沟通管理计划10.风险管理计划11.采购管理计划12.干系人参与计划 点我去AIGIS公众号查看本文 1.项目章程 项目目标成功标准退出标准关键干系人名单发起人…

SpringCloud系列(23)--手写实现负载轮询算法

前言&#xff1a;在上一篇文章中我们介绍了关于负载轮询算法的原理以及看了源代码&#xff0c;而本章节内容则是着重于我们自己手写一个负载轮询算法 1、分别编写provider-payment8001、provider-payment8002这两个子项目的PaymentController类&#xff0c;增加一个/payment/lb…