【whale-starry-stl】01天 list學習筆記

来源:https://www.cnblogs.com/grasscarp7/archive/2023/06/20/17494301.html
-Advertisement-
Play Games

# 一、知識點 ## 1. std::bidirectional_iterator_tag `std::bidirectional_iterator_tag` 是 C++ 標準庫中定義的一個迭代器類型標簽,用於標識支持雙向遍歷的迭代器類型。 在 C++ 中,迭代器是一種泛型指針,用於遍歷容器中的元素 ...


一、知識點

1. std::bidirectional_iterator_tag

std::bidirectional_iterator_tag 是 C++ 標準庫中定義的一個迭代器類型標簽,用於標識支持雙向遍歷的迭代器類型。

在 C++ 中,迭代器是一種泛型指針,用於遍歷容器中的元素。迭代器類型標簽用於標識迭代器的特性,從而在演算法中選擇合適的迭代器類型。

std::bidirectional_iterator_tag 是迭代器類型標簽中的一種,用於標識支持雙向遍歷的迭代器類型。雙向迭代器可以向前和向後遍歷容器中的元素,支持 ++-- 運算符。

標準庫中的許多演算法都要求迭代器支持特定的操作,例如 std::reverse 要求迭代器支持雙向遍歷,因此可以使用 std::bidirectional_iterator_tag 標簽來限定迭代器的類型,從而保證演算法的正確性。

//迭代器類型標簽,用於標識支持雙向遍歷的迭代器類型,支持++和--運算符
typedef std::bidirectional_iterator_tag    iterator_category;

2. ptrdiff_t

ptrdiff_t 是 C++ 標準庫中定義的一個整型類型,用於表示指針之間的差值(即兩個指針在記憶體中的地址差距)。在 C++ 中,指針的加減運算結果是一個 ptrdiff_t 類型的值。

ptrdiff_t 的實現是平臺相關的,通常是一個帶符號的整數類型。在 32 位平臺上,ptrdiff_t 通常是一個 4 位元組的整型,而在 64 位平臺上,ptrdiff_t 通常是一個 8 位元組的整型。

使用 ptrdiff_t 類型可以保證指針操作的安全性,因為指針之間的差值可能超出普通整型的表示範圍。此外,使用 ptrdiff_t 類型還可以提高代碼的可移植性,因為不同平臺上指針的大小可能不同。

//帶符號整型,表示指針之間的差值(即兩個指針在記憶體中的地址差距)
typedef ptrdiff_t                          difference_type;

3. #if __cplusplus >= 201103L

#if __cplusplus >= 201103L這行代碼是C++中的條件編譯指令,意思是如果當前編譯器支持C++11標準或更高版本,則編譯下麵的代碼。其中,__cplusplus是一個C++預定義巨集,表示當前編譯器所支持的C++標準版本,201103L表示C++11標準的版本號。因此,這行代碼的作用是在編譯時檢查當前編譯器是否支持C++11標準或更高版本,如果支持,則編譯下麵的代碼,否則忽略。

4. explicit

explicit 是 C++ 中的一個關鍵字,用於修飾類的構造函數,表示該構造函數是顯式的,不能進行隱式轉換。

在 C++ 中,如果一個類的構造函數只有一個參數,那麼這個構造函數可以被用於隱式轉換。例如,如果有一個類 A 和一個構造函數 A(int),那麼可以使用 A a = 1; 的方式創建一個 A 類型的對象,這裡的 1 會被自動轉換為 A 類型。

使用 explicit 關鍵字可以禁止這種隱式轉換,從而避免一些潛在的問題。例如,如果一個類 B 有一個構造函數 B(int),但是我們希望這個構造函數只能顯式調用,那麼可以將其聲明為 explicit,這樣就不能再使用 B b = 1; 的方式創建一個 B 類型的對象,而必須顯式地調用 B b(1);

另外,explicit 關鍵字還可以用於修飾轉換函數,表示該函數也是顯式的,不能進行隱式轉換。

5. stl_list.h結構

6. 迭代器相關

  • 運算符重載
    重載++(分為++item與item++)

    重載*&

7. 迭代器 traits

  • 模板偏特化(感覺類似java泛型重載)
    image-20230619210901175

  • 完整的iterator_traits
    image-20230619210958775

二、源碼

_List_node_base

主要提供了prev和next指針,以及一些方法。

struct _List_node_base
{
      _List_node_base* _M_next;
      _List_node_base* _M_prev;
      ...
};

_List_node

繼承自_List_node_base,主要提供了一個存放數據的 _M_data

/// An actual node in the %list.
  template<typename _Tp>
    struct _List_node : public __detail::_List_node_base
    {
      ///< User's data.
      _Tp _M_data;
//檢查編譯器是否支持c++11及以上版本
#if __cplusplus >= 201103L
      template<typename... _Args>
        _List_node(_Args&&... __args)     //萬能引用
	: __detail::_List_node_base(), _M_data(std::forward<_Args>(__args)...) //完美轉發
        { }
#endif
    };

_List_iterator

迭代器

1. 必須定義的五個typedef

      //下麵這五個typedef是每個iterator必須有的
      //帶符號整型,表示指針之間的差值(即兩個指針在記憶體中的地址差距)
      typedef ptrdiff_t                          difference_type;
      //迭代器類型標簽,用於標識支持雙向遍歷的迭代器類型,支持++和--運算符
      typedef std::bidirectional_iterator_tag    iterator_category;
      typedef _Tp                                value_type;
      typedef _Tp*                               pointer;
      typedef _Tp&                               reference;

2. 兩個構造器

      _List_iterator() _GLIBCXX_NOEXCEPT
      : _M_node() { }

      explicit //修飾類的構造函數,表示該構造函數是顯式的,不能進行隱式轉換
      _List_iterator(__detail::_List_node_base* __x) _GLIBCXX_NOEXCEPT
      : _M_node(__x) { }      //_M_node:_List_node_base

3. 運算符重載

個人理解,此處之所以能夠向下造型,應該是傳入參數的時候已經做過一次向上造型

// Must downcast from _List_node_base to _List_node to get to _M_data.
      reference
      operator*() const _GLIBCXX_NOEXCEPT 
      { return static_cast<_Node*>(_M_node)->_M_data; }     //_M_node:_List_node_base, _Node:_List_node<_Tp>

      pointer
      operator->() const _GLIBCXX_NOEXCEPT      //2.9版本中是return &(operator*())
      { return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }      //__addressof用於取地址

前++運算符與後++運算符

此處模仿了整數的++i與i++,所以前++運算符需要返回一個引用。

個人理解:與左值和右值有關,如++i是左值,i++是右值。

	  _Self&
      operator++() _GLIBCXX_NOEXCEPT      //++item
      {
	_M_node = _M_node->_M_next;
	return *this;	//由於只有一個成員變數_M_node,因此能返回*this
      }

      _Self
      operator++(int) _GLIBCXX_NOEXCEPT   //item++ 感覺跟左值右值有關
      {
	_Self __tmp = *this;
	_M_node = _M_node->_M_next;
	return __tmp;
      }

_List_base

1. typedef

	  //rebind是一個模板類,它接受一個模板參數T和一個新的類型U,然後定義一個新的類型
      //這個新的類型是將T中的模板參數全部替換為U後得到的結果
      typedef typename _Alloc::template rebind<_List_node<_Tp> >::other
        _Node_alloc_type;

      typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type;

2. _List_impl

這段代碼定義了一個結構體 _List_impl,它是 list 類的一個內部實現,用於存儲 list 對象中的元素和節點信息。

該結構體中使用了兩個模板別名:_Node_alloc_type_Tp_alloc_type,它們分別表示節點和元素的分配器類型。這裡使用了 typename 關鍵字來指示 _Alloc::template rebind<_List_node<_Tp> >::other_Alloc::template rebind<_Tp>::other 是類型別名,而不是靜態成員變數或函數。

接下來,_List_impl 結構體中定義了一個 _M_node 成員變數,它是一個 _List_node_base 類型的對象,用於存儲 list 對象中的頭節點和尾節點信息。

_List_impl 結構體還有三個構造函數,分別用於構造一個空的 _List_impl 對象、使用指定的節點分配器構造 _List_impl 對象,以及使用右值引用的節點分配器構造 _List_impl 對象。

需要註意的是,_List_impl 結構體中的 _Node_alloc_type_Tp_alloc_type 類型別名都是使用 _Alloc 類型的 template rebind 成員模板生成的,這是因為 list 類需要支持自定義分配器,因此需要使用 rebind 將原始分配器重新綁定到節點和元素類型上。

      struct _List_impl
      : public _Node_alloc_type
      {
	__detail::_List_node_base _M_node;

	_List_impl()
	: _Node_alloc_type(), _M_node()
	{ }

	_List_impl(const _Node_alloc_type& __a) _GLIBCXX_NOEXCEPT   //底層const
	: _Node_alloc_type(__a), _M_node()
	{ }

#if __cplusplus >= 201103L
	_List_impl(_Node_alloc_type&& __a) _GLIBCXX_NOEXCEPT  //右值引用
	: _Node_alloc_type(std::move(__a)), _M_node()   //轉為右值,移動語義
	{ }
#endif
      };

3. 構造函數

這是 list 類的一個基類 _List_base,包含了 list 類的一些基本實現。這裡的三個構造函數都是 _List_base 類的構造函數。

第一個構造函數 _List_base() 是預設構造函數,它使用預設的節點分配器構造了一個 _List_impl 對象 _M_impl,並調用了 _M_init() 函數來初始化 _List_base 對象。

第二個構造函數 _List_base(const _Node_alloc_type& __a) 則接受一個節點分配器 __a,用於構造一個 _List_impl 對象 _M_impl,並調用了 _M_init() 函數來初始化 _List_base 對象。

第三個構造函數 _List_base(_List_base&& __x) 是移動構造函數,它接受一個右值引用 __x,用於構造一個 _List_impl 對象 _M_impl。在構造過程中,它使用 __x._M_get_Node_allocator() 獲取 __x 對象的節點分配器,並將其作為參數傳遞給 _M_impl 的構造函數,從而實現了節點分配器的移動。接著,它調用了 _M_init() 函數來初始化 _List_base 對象,並使用 __detail::_List_node_base::swap 函數交換了 _M_impl._M_node__x._M_impl._M_node 兩個節點的信息,實現了 _List_base 對象的移動構造。需要註意的是,該構造函數只在 C++11 及以上版本中被定義。

      _List_base()
      : _M_impl()
      { _M_init(); }

      _List_base(const _Node_alloc_type& __a) _GLIBCXX_NOEXCEPT
      : _M_impl(__a)
      { _M_init(); }

#if __cplusplus >= 201103L
      _List_base(_List_base&& __x) noexcept     //右值引用
      : _M_impl(std::move(__x._M_get_Node_allocator()))     //移動構造函數
      {
	_M_init();
	__detail::_List_node_base::swap(_M_impl._M_node, __x._M_impl._M_node);
      }
#endif

4. _M_init()

	  void
      _M_init() _GLIBCXX_NOEXCEPT
      {
        //頭節點的prev與next指向自身
        this->_M_impl._M_node._M_next = &this->_M_impl._M_node;
        this->_M_impl._M_node._M_prev = &this->_M_impl._M_node;
      }

list

1. typedef

      // concept requirements
      typedef typename _Alloc::value_type                _Alloc_value_type;
      __glibcxx_class_requires(_Tp, _SGIAssignableConcept)
      __glibcxx_class_requires2(_Tp, _Alloc_value_type, _SameTypeConcept)

      typedef _List_base<_Tp, _Alloc>                    _Base;
      typedef typename _Base::_Tp_alloc_type		 _Tp_alloc_type;
      typedef typename _Base::_Node_alloc_type		 _Node_alloc_type;

    public:
      typedef _Tp                                        value_type;
      typedef typename _Tp_alloc_type::pointer           pointer;
      typedef typename _Tp_alloc_type::const_pointer     const_pointer;
      typedef typename _Tp_alloc_type::reference         reference;
      typedef typename _Tp_alloc_type::const_reference   const_reference;
      typedef _List_iterator<_Tp>                        iterator;
      typedef _List_const_iterator<_Tp>                  const_iterator;
      typedef std::reverse_iterator<const_iterator>      const_reverse_iterator;
      typedef std::reverse_iterator<iterator>            reverse_iterator;
      typedef size_t                                     size_type;
      typedef ptrdiff_t                                  difference_type;
      typedef _Alloc                                     allocator_type;

    protected:
      // Note that pointers-to-_Node's can be ctor-converted to
      // iterator types.
      typedef _List_node<_Tp>				 _Node;

      using _Base::_M_impl;
      using _Base::_M_put_node;
      using _Base::_M_get_node;
      using _Base::_M_get_Tp_allocator;
      using _Base::_M_get_Node_allocator;

2. 一些函數

_M_create_node

      template<typename... _Args>
        _Node*
        _M_create_node(_Args&&... __args)	//萬能引用
	{
	  _Node* __p = this->_M_get_node();       //獲取新的節點指針
	  __try
	    {
	      _M_get_Node_allocator().construct(__p,
						std::forward<_Args>(__args)...); //使用完美轉發構造節點中的數據
	    }
	  __catch(...)
	    {
	      _M_put_node(__p);
	      __throw_exception_again;
	    }
	  return __p;	//返回指針
	}

assign

assign 函數是 list 類的成員函數,用於將新的元素賦值給當前的 list 對象。該函數有多個重載版本,其中一個版本接受一個初始化列表作為參數,用於將初始化列表中的元素賦值給當前的 list 對象。

具體地,該函數接受一個初始化列表 __l,它將 __l 中的元素替換當前 list 對象中的元素。為此,它調用另一個 assign 函數,該函數接受兩個迭代器參數,分別指向初始化列表的第一個元素和最後一個元素。assign 函數用這些元素來替換當前 list 對象中的元素。

需要註意的是,該函數只在 C++11 及以上版本中被定義。

#if __cplusplus >= 201103L
      /**
       *  @brief  Assigns an initializer_list to a %list.
       *  @param  __l  An initializer_list of value_type.
       *
       *  Replace the contents of the %list with copies of the elements
       *  in the initializer_list @a __l.  This is linear in __l.size().
       */
      void
      assign(initializer_list<value_type> __l)
      { this->assign(__l.begin(), __l.end()); }
#endif

3. 構造函數

第一個構造函數 list() 是預設構造函數,它創建一個空的 list 對象,使用預設的節點分配器。

第二個構造函數 list(const allocator_type& __a) 接受一個節點分配器 __a,用於創建一個空的 list 對象。

第三個構造函數 list(size_type __n) 接受一個整數參數 __n,用於創建一個包含 __n 個預設構造的元素的 list 對象。

第四個構造函數 list(size_type __n, const value_type& __value, const allocator_type& __a = allocator_type()) 接受一個整數參數 __n 和一個元素值 __value,用於創建一個包含 __n 個值為 __value 的元素的 list 對象。

第五個構造函數 list(const list& __x) 是拷貝構造函數,用於創建一個與 __x 相同的 list 對象。

第六個構造函數 list(list&& __x) noexcept 是移動構造函數,用於創建一個與 __x 相同的 list 對象,並將 __x 中的元素移動到新的 list 對象中。

第七個構造函數 list(initializer_list<value_type> __l, const allocator_type& __a = allocator_type()) 接受一個初始化列表 __l,用於創建一個包含 __l 中元素的 list 對象。

第八個構造函數 list(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) 接受兩個迭代器參數 __first__last,用於創建一個包含從 __first__last 的所有元素的 list 對象。需要註意的是,該構造函數只在 C++11 及以上版本中被定義。

list()
#if __cplusplus >= 201103L
      noexcept(is_nothrow_default_constructible<_Node_alloc_type>::value) //判斷是否支持預設構造函數
#endif
      : _Base() { }

      /**
       *  @brief  Creates a %list with no elements.
       *  @param  __a  An allocator object.
       */
      explicit
      list(const allocator_type& __a) _GLIBCXX_NOEXCEPT
      : _Base(_Node_alloc_type(__a)) { }

#if __cplusplus >= 201103L
      /**
       *  @brief  Creates a %list with default constructed elements.
       *  @param  __n  The number of elements to initially create.
       *
       *  This constructor fills the %list with @a __n default
       *  constructed elements.
       */
      explicit
      list(size_type __n)
      : _Base()
      { _M_default_initialize(__n); }

      /**
       *  @brief  Creates a %list with copies of an exemplar element.
       *  @param  __n  The number of elements to initially create.
       *  @param  __value  An element to copy.
       *  @param  __a  An allocator object.
       *
       *  This constructor fills the %list with @a __n copies of @a __value.
       */
      list(size_type __n, const value_type& __value,
	   const allocator_type& __a = allocator_type())
      : _Base(_Node_alloc_type(__a))
      { _M_fill_initialize(__n, __value); }
#else
      /**
       *  @brief  Creates a %list with copies of an exemplar element.
       *  @param  __n  The number of elements to initially create.
       *  @param  __value  An element to copy.
       *  @param  __a  An allocator object.
       *
       *  This constructor fills the %list with @a __n copies of @a __value.
       */
      explicit
      list(size_type __n, const value_type& __value = value_type(),
	   const allocator_type& __a = allocator_type())
      : _Base(_Node_alloc_type(__a))
      { _M_fill_initialize(__n, __value); }
#endif

      /**
       *  @brief  %List copy constructor.
       *  @param  __x  A %list of identical element and allocator types.
       *
       *  The newly-created %list uses a copy of the allocation object used
       *  by @a __x.
       */
      list(const list& __x)
      : _Base(__x._M_get_Node_allocator())
      { _M_initialize_dispatch(__x.begin(), __x.end(), __false_type()); }

#if __cplusplus >= 201103L
      /**
       *  @brief  %List move constructor.
       *  @param  __x  A %list of identical element and allocator types.
       *
       *  The newly-created %list contains the exact contents of @a __x.
       *  The contents of @a __x are a valid, but unspecified %list.
       */
      list(list&& __x) noexcept
      : _Base(std::move(__x)) { }

      /**
       *  @brief  Builds a %list from an initializer_list
       *  @param  __l  An initializer_list of value_type.
       *  @param  __a  An allocator object.
       *
       *  Create a %list consisting of copies of the elements in the
       *  initializer_list @a __l.  This is linear in __l.size().
       */
      list(initializer_list<value_type> __l,
           const allocator_type& __a = allocator_type())
      : _Base(_Node_alloc_type(__a))
      { _M_initialize_dispatch(__l.begin(), __l.end(), __false_type()); }
#endif

      /**
       *  @brief  Builds a %list from a range.
       *  @param  __first  An input iterator.
       *  @param  __last  An input iterator.
       *  @param  __a  An allocator object.
       *
       *  Create a %list consisting of copies of the elements from
       *  [@a __first,@a __last).  This is linear in N (where N is
       *  distance(@a __first,@a __last)).
       */
#if __cplusplus >= 201103L
      template<typename _InputIterator,
	       typename = std::_RequireInputIter<_InputIterator>>
        list(_InputIterator __first, _InputIterator __last,
	     const allocator_type& __a = allocator_type())
	: _Base(_Node_alloc_type(__a))
        { _M_initialize_dispatch(__first, __last, __false_type()); }
#else

4. 運算符重載

移動賦值運算符

      list&
      operator=(list&& __x)   //移動賦值運算符
      {
        // NB: DR 1204.
        // NB: DR 675.
        this->clear();
        this->swap(__x);
        return *this;
      }

初始化列表

這是 list 類的賦值運算符重載,它允許使用初始化列表來為 list 對象賦值。

該運算符重載接受一個初始化列表 __l,它將 __l 中的元素賦值給當前的 list 對象。具體地,它調用 assign 函數,該函數接受兩個迭代器參數,分別指向初始化列表的第一個元素和最後一個元素。assign 函數用這些元素來替換當前 list 對象中的元素。

該運算符重載返回一個 list 的引用,以支持鏈式賦值。

	 list&
      operator=(initializer_list<value_type> __l)
      {
	this->assign(__l.begin(), __l.end());
	return *this;
      }

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在JavaScript中,數組本質上是一種特殊的對象,它的類型值會返回 `object`。 如果我們需要比較兩個數組是否相等,不能像比較基本類型(String、Number、Boolean等)一樣,使用 ` ` (或 `==`) 來判斷,所以如果要比較數組是否相等,需要使用一些特殊方法。關於JS類型 ...
  • 一、約定大於配置 泰思勒定律也被稱為複雜度守恆定律。該定律指出每一個過程都有其固有的複雜性,存在一個臨界點,超過了這個點過程就不能再簡化了,你只能將固有的複雜性從一個地方移動到另外一個地方。 根據這個定律,在做系統設計時,預設會給用戶一個“套餐”,這個套餐會滿足多數人的需求。實在不滿足需求再特殊配置 ...
  • 代理模式是23種設計模式中結構型模式的一種,它的核心是通過代理類來完成其他對象的訪問,降低訪問者和被訪問者的耦合度,也對功能進行了增強。 ...
  • 在日常開發中會經常遇到一些需要非同步定時執行的業務訴求,典型的使用場景如:超時未支付訂單關單、每隔 2h 更新好友排行榜、3.22 日 17 點《xx》劇上線等。目前業務側多基於以下思路來快速搭建一個調度系統,mysql 或者 redis 隊列存儲待執行任務,通過 crontab 定時觸發應用完成“撈... ...
  • # 第01章-準備工作 ## 1、微信支付產品介紹 參考資料:[產品中心 - 微信支付商戶平臺 (qq.com)](https://pay.weixin.qq.com/static/product/product_index.shtml#payment_product) 付款碼支付、JSAPI支付、 ...
  • 某日二師兄參加XXX科技公司的C++工程師開發崗位第20面: > 面試官:C++中支持哪些類型轉換? > > 二師兄:C++支持C風格的類型轉換,併在C++11引入新的關鍵字規範了類型轉換。 > > 二師兄:C++11引入四種新的類型轉換,分別是`static_cast`、`dynamic_cast ...
  • 隨著AI大模型的浪潮席卷全球,如今的AI技術已經顛覆了大家對傳統AI的認識,微軟更是用瀏覽器與搜索引擎上的實踐,證明瞭當今的AI技術具備打破行業格局的能力。 對於我們應用開發者來說,AI基建的建設與競爭是無法參與的,但在AI的應用領域依然大有可為!目前,國內各大科技公司已經陸續推出了各自的AI大模型 ...
  • @Data public class ExeclDto { /** * execl表 */ private String filename; /** * 需要匹配的工作表名 */ private String name1; /** * 需要匹配的工作表名 */ private String name ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...