STL之list容器的介绍与模拟实现+适配器

STL之list容器的介绍与模拟实现+适配器

  • 1. list的介绍
  • 2. list容器的使用
    • 2.1 list的定义
    • 2.2 list iterator的使用
    • 2.3 list capacity
    • 2.4 list element access
    • 2.5 list modifiers
    • 2.6 list的迭代器失效
  • 3. list的模拟实现
    • 3.1 架构搭建
    • 3.2 迭代器
      • 3.2.1 正向迭代器
      • 3.2.2反向迭代器+适配器
    • 3.3 空间控制模块
    • 3.4 数据的访问
    • 3.5 增加/删除数据
    • 3.6 构造/拷贝构造/析构
  • 4. 整体代码逻辑

所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️

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来说这
    可能是一个重要的因素)

2. list容器的使用

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

2.2 list iterator的使用

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

【注意】

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

2.3 list capacity

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

2.4 list element access

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

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中的有效元素

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())
	{
		it = l.erase(it);
	}
}

3. list的模拟实现

3.1 架构搭建

  • 首先,list容器的底层实现是一个双向循环链表。所以在实现本章节的前提下,我们首先要熟知我们学习C语言的时候是怎么实现一个带头双向循环链表的。核心的逻辑思维是一模一样的。
  • 所以在实现的前提下,我们可以先从C语言数据结构着手起步。
  • 整体的构架就是:1. 要有一个节点的类,里面包含了两个指针next和prev,和一个存放数据的变量val。2. 就是构建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 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<const_iterator, const T&, const T*> const_reverse_iterator;

  public:
      ///
      // List的构造
      list()
      {……}

      //拷贝构造
      list(const list<T>& l)
      {……}
      
      ~list()
      {……}
//………………
  private:
  	//创建新节点
      void CreateHead()
      {
          _pHead = new Node;
          _pHead->_pPre = _pHead;
          _pHead->_pNext = _pHead;
      }
      Node* _pHead;
}

3.2 迭代器

同样提供两个版本const 和 非const版本的。

  • 但是这里要注意一点就是,我们的迭代器在进行移动的时候无非就是++/–操作,但是我们可由直接进行(iterator)a++吗?如果这是个内置类型的话那自然是可以的。但是组成list容器并非是内置类型,而是自定义类型,每个节点都是一个类,而自定义类型是无法直接进行++/–等一些类的运算符操作的。要想对自定义类型进行运算符操作就必须要使用运算符重载函数。所以为了可以对迭代器进行运算符操作就也要定义一个迭代器类,并在类中包含要进行操作的自定义类型的对象,在类中进行运算符操作。

3.2.1 正向迭代器

template<class T, class Ref, class Ptr>
class ListIterator
{
public:
    typedef ListNode<T> PNode;
    typedef ListIterator<T, Ref, Ptr> Self; 

    PNode* _pNode;

    ListIterator(PNode* pNode = nullptr)
        :_pNode(pNode)
    {}

    //ListIterator(const Self& l);
    Ref operator*()
    {
        return _pNode->_val;
    }

    Ptr operator->()
    {
        return &_pNode->_val;
    }
    Self& operator++()
    {
        _pNode = _pNode->_pNext;
        return *this;
    }

    Self operator++(int)
    {
        Self tmp(*this);//拷贝构造(浅拷贝)
        _pNode = _pNode->_pNext;
        return tmp;
    }
    Self& operator--()
    {
        _pNode = _pNode->_pPre;
        return *this;
    }
    Self& operator--(int)
    {
        Self tmp(*this);
        _pNode = _pNode->_pPre;
        return tmp;
    }
    bool operator!=(const Self& l)
    {
        return _pNode != l._pNode;
    }
    bool operator==(const Self& l)
    {
        return _pNode == l._pNode;
    }
};
  • 注:这里我们可以看到我们创建ListIterator类的时候使用了类模板,并且看到模板参数中不止一个参数,而是多了两个Ref和Ptr。至于理由是:我们要实现两个版本的迭代器,一个是const和非const版本的,而这两个版本的区别无非就是返回的引用值是否能被修改该,也就是重载*解引用的时候,const版本返回的是const T&,非const版本返回的就是T&,除了这个其他的地方都一样。那如果想两个都实现是不是就要copy一份呢?一下写两个出来呢?所以这个时候模板参数起作用了。定义Ref模板参数,不管是T&还是const T&我们都返回Ref只是当我们想调用const版本的时候就给模板传const T&的类型,我们想调用非cosnt版本的时候就传T&类型的给模板就行,这样就可以避免代码重复问题,提高复用度。
  • 同样的既然我们定义是一个自定义类型的节点,迭代器可以看作是一个指针,有了自定义类型和之指针我们就可以通过指针+ (->)的方式拿到节点里面的val值,但是->同样也有两个版本,所以做法和上面的是一样的,只需要多个模板参数传一个值Ptr就行,具体调用什么版本的就传什么类型的过去就行。
  • 这里同样也要注意的一点就是重载(->)的时候,我们是这样定义的:
    Ptr operator->() { return &_pNode->_val; }
    本来我们使用的时候应该it->->val这样有两个箭头使用,也就是it.operator->()->val这样去使用的,但是编译为了方便使用做了优化只需要一个箭头就可进行访问了。

3.2.2反向迭代器+适配器

适配器:
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总
结),该种模式是将一个类的接口转换成客户希望的另外一个接口
在这里插入图片描述
用简单话来概括适配器就是——用现有的东西适配出一个新的东西。

我们的反向迭代器其实就是用到了适配器的概念,用到就是正向迭代器适配出来的。
在这里插入图片描述

  • rbegin的++就是end的–,rend的–就是begin的++,要取rbegin指向的值就是–end()在解引用得到。
    所以我们只需要讲正向迭代器的类型当作参数传个我们的反向迭代器的类模板参数就行。
  • 同样的这里也需要传三个模板参数,原理和上面是一样的。
template<class Iterator, class Pef, class Ptr>
struct Reverse_iterator
{
	typedef Reverse_iterator<Iterator, Pef, Ptr> self;

	Iterator cur;

	Reverse_iterator(Iterator it)
		:cur(it)
	{}

	Ptr operator->()
	{
		return &(operator*());
	}

	self& operator++()
	{
		--cur;
		return *this;
	}

	self operator++(int)
	{
		Iterator tmp = cur;
		--cur;
		return tmp;
	}

	self& operator--()
	{
		++cur;
		return *this;
	}

	self operator--(int)
	{
		Iterator tmp = cur;
		++cur;
		return tmp;
	}

	Pef operator*()
	{
		Iterator tmp = cur;
		--tmp;
		return *tmp;
	}

	bool operator!=(const self& l)
	{
		return cur != l.cur;
	}

	bool operator==(const self& l)
	{
		return cur == l.cur;
	}
};

3.3 空间控制模块

  1. 返回list容器的大小
size_t size() const
{
    size_t len = 0;
    const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了
    while (it != end())
    {
        ++len;
        ++it;
    }
    return len;
}
  1. list容器是否为空
bool empty()const
{
    return begin() == end();
}
  1. resize
void resize(size_t newsize, const T& data = T())
{
	size_t oldsize = size();
	if (newsize <= oldsize)
	{
		// 有效元素个数减少到newsize
		while (newsize < oldsize)
		{
			pop_back();
			oldsize--;
		}
	}
	else
	{
		while (oldsize < newsize)
		{
			push_back(data);
			oldsize++;
		}
	}
}

3.4 数据的访问

  1. 访问头节点的值
T& front()
{
    return (begin()._pNode)->_pNext->_val;
}
const T& front()const
{
    return (begin()._pNode)->_pNext->_val;

}
  1. 访问尾节点的值
T& back()
{
    return (end()._pNode)->_pPre->_val;
}
const T& back()const
{
    return (end()._pNode)->_pPre->_val;
}

3.5 增加/删除数据

  1. 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    Node* newnode = new Node;
    newnode->_val = val;
    Node* cur = pos._pNode;
    Node* prev = cur->_pPre;

    prev->_pNext = newnode;
    newnode->_pPre = prev;
    newnode->_pNext = cur;
    cur->_pPre = newnode;

    return iterator(newnode);
}
  1. 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
        {
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;
            Node* next = cur->_pNext;

            prev->_pNext = next;
            next->_pPre = prev;

            return iterator(next);
        }
  1. push_back/pop_back
void push_back(const T& val) 
{
    insert(end(), val); 
}
void pop_back() 
{ 
    erase(--end()); 
}
  1. push_front/pop_front
void push_front(const T& val) 
{ 
    insert(begin(), val); 
}
void pop_front() 
{ 
    erase(begin()); 
}
  1. 清除数据
void clear()
{
    iterator it = begin();
    while (it != end())
    {
        it = erase(it);
    }
}

3.6 构造/拷贝构造/析构

  1. 构造函数
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;
    }
}
  1. 拷贝构造
list(const list<T>& l)
{
    CreateHead();//先创建哨兵位
    const_iterator it = l.begin();
    while (it != l.end())
    {
        push_back(*it);
        ++it;
    }
}
  1. 赋值运算符重载
void swap(list<T>& l)
{
    std::swap(_pHead, l._pHead);
}

list<T>& operator=(list<T> l) 
{
    swap(l);
    return *this;
}
  1. 析构
~list()
{
    //iterator it = begin();
    //while (it != end())
    //{
    //    Node* cur = it._pNode;
    //    _pHead->_pNext = cur->_pNext;
    //    delete cur;
    //}

    clear();
    delete _pHead;
    _pHead = nullptr;
}

4. 整体代码逻辑

namespace qfw
{
    // 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>
    class ListIterator
    {
    public:
        typedef ListNode<T> PNode;
        typedef ListIterator<T, Ref, Ptr> Self; 

        PNode* _pNode;

        ListIterator(PNode* pNode = nullptr)
            :_pNode(pNode)
        {}

        //ListIterator(const Self& l);
        Ref operator*()
        {
            return _pNode->_val;
        }

        Ptr operator->()
        {
            return &_pNode->_val;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }

        Self operator++(int)
        {
            Self tmp(*this);//拷贝构造(浅拷贝)
            _pNode = _pNode->_pNext;
            return tmp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self tmp(*this);
            _pNode = _pNode->_pPre;
            return tmp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;
        }
    };

	template<class Iterator, class Pef, class Ptr>
	struct Reverse_iterator
	{
		typedef Reverse_iterator<Iterator, Pef, Ptr> self;
	
		Iterator cur;
	
		Reverse_iterator(Iterator it)
			:cur(it)
		{}
	
		Ptr operator->()
		{
			return &(operator*());
		}
	
		self& operator++()
		{
			--cur;
			return *this;
		}
	
		self operator++(int)
		{
			Iterator tmp = cur;
			--cur;
			return tmp;
		}
	
		self& operator--()
		{
			++cur;
			return *this;
		}
	
		self operator--(int)
		{
			Iterator tmp = cur;
			++cur;
			return tmp;
		}
	
		Pef operator*()
		{
			Iterator tmp = cur;
			--tmp;
			return *tmp;
		}
	
		bool operator!=(const self& l)
		{
			return cur != l.cur;
		}
	
		bool operator==(const self& l)
		{
			return cur == l.cur;
		}
	};

    //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<const_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();//先创建哨兵位
            const_iterator it = l.begin();
            while (it != l.end())
            {
                push_back(*it);
                ++it;
            }
        }

        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
        }

        list<T>& operator=(list<T> l) 
        {
            swap(l);
            return *this;
        }
        ~list()
        {
            //iterator it = begin();
            //while (it != end())
            //{
            //    Node* cur = it._pNode;
            //    _pHead->_pNext = cur->_pNext;
            //    delete cur;
            //}

            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 len = 0;
            const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了
            while (it != end())
            {
                ++len;
                ++it;
            }
            return len;
        }
        bool empty()const
        {
            return begin() == end();
        }

        void resize(size_t newsize, const T& data = T())
        {
            size_t oldsize = size();
            if (newsize <= oldsize)
            {
                // 有效元素个数减少到newsize
                while (newsize < oldsize)
                {
                    pop_back();
                    oldsize--;
                }
            }
            else
            {
                while (oldsize < newsize)
                {
                    push_back(data);
                    oldsize++;
                }
            }
        }

        
        // List Access
        T& front()
        {
            return (begin()._pNode)->_pNext->_val;
        }
        const T& front()const
        {
            return (begin()._pNode)->_pNext->_val;

        }
        T& back()
        {
            return (end()._pNode)->_pPre->_val;
        }
        const T& back()const
        {
            return (end()._pNode)->_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)
        {
            Node* newnode = new Node;
            newnode->_val = val;
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;

            prev->_pNext = newnode;
            newnode->_pPre = prev;
            newnode->_pNext = cur;
            cur->_pPre = newnode;

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

            prev->_pNext = next;
            next->_pPre = prev;

            return iterator(next);
        }
        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        Node* _pHead;
    };

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习技巧应用36-深度学习模型训练中的超参数调优指南大全,总结相关问题与答案

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用36-深度学习模型训练中的超参数调优指南大全,总结相关问题与答案。深度学习模型训练中的调优指南大全概括了数据预处理、模型架构设计、超参数优化、正则化策略和训练技巧等多个关键方面,以提升模型性能和泛化能力。 …

申请SSL证书怎么进行域名验证?域名验证的三种方式

SSL证书是用于加密和保护Web服务器和浏览器之间通信的数字证书&#xff0c;在申请SSL证书时&#xff0c;为了防止域名被冒用&#xff0c;对于申请SSL证书的域名&#xff0c;要求先验证这个域名的所有权。而目前可用的域名验证SSL证书方式有三种&#xff1a;分别是DNS验证、邮箱…

展示wandb的数据

import wandb import matplotlib.pyplot as plt# 初始化 wandb API api wandb.Api()# 假设您想要访问的项目名为 my_project&#xff0c;并且您的 wandb 用户名为 my_username project_name "aicolab/RWKV-5-Test"# 获取项目中的runs runs api.runs(project_name)…

【MySQL】-12 MySQL索引(上篇MySQL索引类型前置-1)

MySQL索引 索引1 索引基础2 索引与优化1 选择索引的数据类型1.1 选择标识符 2 索引入门2.1 索引的类型2.1.1 B-Tree索引2.1.2 Hash索引2.1.3 空间(R-Tree)索引2.1.4 全文(Full-text)索引 索引的优点&#xff1a;索引是最好的解决方案吗&#xff1f; 索引 索引&#xff08;在MYS…

【51单片机】LCD1602(可视化液晶屏)调试工具的使用

前言 大家好吖&#xff0c;欢迎来到 YY 滴 单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY…

Stable Video Diffusion图片转视频——Stability AI开源视频模型

我们前期介绍过Stable Diffusion&#xff0c;stable diffusion模型是Stability AI开源的一个text-to-image的扩散模型&#xff0c;其模型在速度与质量上面有了质的突破&#xff0c;玩家们可以在自己消费级GPU上面来运行此模型。 文生图大模型已经火了很长一段时间了&#xff0c…

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090 2024/2/10 17:54 【使用剪映识别不同的封装格式&#xff0c;不同的音视频编码&#xff0c;对GPU的占用率可能会有比较大的不同&#xff01;】 很容易发现在在WIN10下使用剪映的时候&#xff0c;X99RX550组合。 GPU部分&…

Stable Diffusion 模型下载:RealCartoon-Realistic - V13

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 该检查点是 RealCartoon3D 检查点的一个分支。这个目标是在背景和人物中产生更“真实”的外观。我试图避免这个模型中更多的动漫、卡通和“完美”外观。这是一个肯

Linux运行级别 | 管理Linux服务

Linux运行级别 级别&#xff1a; 0关机1单用户2多用户但是不运行nfs网路文件系统3默认的运行级别&#xff0c;给一个黑的屏幕&#xff0c;只能敲命令4未使用5默认的运行级别&#xff0c;图形界面6重启切换运行级别&#xff1a; init x管理Linux服务 systemctl命令&#xf…

〖大前端 - ES6篇②〗- let和const

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…

TELNET 远程终端协议

远程终端协议 TELNET TELNET 是一个简单的远程终端协议&#xff0c;也是互联网的正式标准。 用户用 TELNET 就可在其所在地通过 TCP 连接注册&#xff08;即登录&#xff09;到远地的另一个主机上&#xff08;使用主机名或 IP 地址&#xff09;。 TELNET 能将用户的击键传到…

刘谦魔术我用代码还原了,魔术尽头是数学,数学尽头是神学!

刘谦在春晚让两个半张扑克牌合在一起的时候&#xff0c;我就知道其中必然有数学的奥妙。 假设我们初始卡牌为1&#xff0c;2&#xff0c;3&#xff0c;4。对半撕开后我们定义扑克牌为&#xff1a; 1(1) 2(1) 3(1) 4(1) 1(2) 2(2) 3(2) 4(2)按照刘谦的魔术&#xff0c;你需要…

揭秘企业内团队协作的隐形障碍

企业内团队协作是现代企业中不可避免的一部分。然而在团队协作中&#xff0c;总是会存在一些障碍&#xff0c;这也是企业内团队协作面临的一些挑战。这些障碍会对企业的效率、生产力和团队士气产生影响&#xff0c;因此一定要在团队合作中积极地寻找和消除这些障碍。 一、缺乏透…

华为配置交换机KPI信息上报分析器示例组网图形

配置交换机KPI信息上报分析器示例 组网图形 图1 KPI信息上报拓扑图 组网需求操作步骤配置文件 组网需求 如图1所示&#xff0c;某企业网络用一台华为公司iMaster NCE-CampusInsight作为分析器对交换机设备进行智能运维管理。iMaster NCE-CampusInsight与交换机之间已经实现路由…

2024年 复习 HTML5+CSS3+移动web 笔记 之CSS遍 第6天

6.1 定位-相对和绝对和固定 6.2 相对和绝对和固定 6.3 堆叠顺序z-index 6.4 定位总结 6.5 CSS精灵 基本使用 6.6 案例 CSS精灵 京东服务 6.7 字体图标-下载和使用 6.8 字体图标-上传 6.9 垂直对齐方式vertical-align 6.10 过渡属性 6.11 修饰属性-透明度与光标类型 6.12 综合案…

从0开始图形学(光栅化)

前言 说起图形学&#xff0c;很多人就会提到OpenGL&#xff0c;但其实两者并不是同一个东西。引入了OpenGL加重了学习的难度和成本&#xff0c;使得一些原理并不直观。可能你知道向量&#xff0c;矩阵&#xff0c;纹理&#xff0c;重心坐标等概念&#xff0c;但就是不知道这些概…

Kong 负载均衡

负载均衡是一种将API请求流量分发到多个上游服务的方法。负载均衡可以提高整个系统的响应速度&#xff0c;通过防止单个资源过载而减少故障。 在以下示例中&#xff0c;您将使用部署在两台不同服务器或上游目标上的应用程序。Kong网关需要在这两台服务器之间进行负载均衡&…

[职场] 职场上该如何和同事相处呢?七种方法教你和同事友好相处 #其他#媒体

职场上该如何和同事相处呢&#xff1f;七种方法教你和同事友好相处 在职场上&#xff0c;如何和同事相处是一堂必修课。同事&#xff0c;是我们天天必须看到的人&#xff0c;只有和同事友好相处&#xff0c;我们才能生活得更好&#xff0c;工作得更好。那么&#xff0c;我们在…

小巨人大爆发:紧凑型大型语言模型效率之谜揭晓!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Linux系统基础 03 IP地址虚拟网络、Linux软件包管理、ssh服务、apache服务和samba服务的简单搭建

文章目录 一、IP地址虚拟网络二、Linux软件包管理1、rpm包管理器2、yum包管理器3、源码安装 三、ssh服务四、apache服务五、samba服务 一、IP地址虚拟网络 1、IP地址格式是点分十进制&#xff0c;例&#xff1a;172.16.45.10。即4段8位二进制 2、IP地址分为网络位和主机位。网…