zoukankan      html  css  js  c++  java
  • STL-Vector源码剖析

      1 G++ 2.91.57,cygnuscygwin-b20includeg++stl_vector.h 完整列表
      2 /*
      3  *
      4  * Copyright (c) 1994
      5  * Hewlett-Packard Company
      6  *
      7  * Permission to use, copy, modify, distribute and sell this software
      8  * and its documentation for any purpose is hereby granted without fee,
      9  * provided that the above copyright notice appear in all copies and
     10  * that both that copyright notice and this permission notice appear
     11  * in supporting documentation.  Hewlett-Packard Company makes no
     12  * representations about the suitability of this software for any
     13  * purpose.  It is provided "as is" without express or implied warranty.
     14  *
     15  *
     16  * Copyright (c) 1996
     17  * Silicon Graphics Computer Systems, Inc.
     18  *
     19  * Permission to use, copy, modify, distribute and sell this software
     20  * and its documentation for any purpose is hereby granted without fee,
     21  * provided that the above copyright notice appear in all copies and
     22  * that both that copyright notice and this permission notice appear
     23  * in supporting documentation.  Silicon Graphics makes no
     24  * representations about the suitability of this software for any
     25  * purpose.  It is provided "as is" without express or implied warranty.
     26  */
     27 
     28 /* NOTE: This is an internal header file, included by other STL headers.
     29  *   You should not attempt to use it directly.
     30  */
     31 
     32 #ifndef __SGI_STL_INTERNAL_VECTOR_H
     33 #define __SGI_STL_INTERNAL_VECTOR_H
     34 
     35 __STL_BEGIN_NAMESPACE 
     36 
     37 #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
     38 #pragma set woff 1174
     39 #endif
     40 
     41 template <class T, class Alloc = alloc>  // 預設使用 alloc 為配置器
     42 class vector {
     43 public:
     44   // 以下標示 (1),(2),(3),(4),(5),代表 iterator_traits<I> 所服務的5個型別。
     45   typedef T value_type;                // (1)
     46   typedef value_type* pointer;             // (2)
     47   typedef const value_type* const_pointer;
     48   typedef const value_type* const_iterator;
     49   typedef value_type& reference;         // (3)
     50   typedef const value_type& const_reference;
     51   typedef size_t size_type;
     52   typedef ptrdiff_t difference_type;     // (4)
     53   // 以下,由於vector 所維護的是一個連續線性空間,所以不論其元素型別為何,
     54   // 原生指標都可以做為其迭代器而滿足所有需求。
     55   typedef value_type* iterator;
     56   /* 根據上述寫法,如果客端寫出這樣的碼:
     57       vector<Shape>::iterator is;
     58       is 的型別其實就是Shape*
     59       而STL 內部運用 iterator_traits<is>::reference 時,獲得 Shape&
     60                  運用iterator_traits<is>::iterator_category 時,獲得 
     61                       random_access_iterator_tag        (5)
     62       (此乃iterator_traits 針對原生指標的特化結果)
     63   */
     64 
     65 #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
     66   typedef reverse_iterator<const_iterator> const_reverse_iterator;
     67   typedef reverse_iterator<iterator> reverse_iterator;
     68 #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
     69   typedef reverse_iterator<const_iterator, value_type, const_reference, 
     70                            difference_type>  const_reverse_iterator;
     71   typedef reverse_iterator<iterator, value_type, reference, difference_type>
     72           reverse_iterator;
     73 #endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
     74 protected:
     75   // 專屬之空間配置器,每次配置一個元素大小
     76   typedef simple_alloc<value_type, Alloc> data_allocator;
     77 
     78   // vector採用簡單的線性連續空間。以兩個迭代器start和end分別指向頭尾,
     79   // 並以迭代器end_of_storage指向容量尾端。容量可能比(尾-頭)還大,
     80   // 多餘即備用空間。
     81   iterator start;
     82   iterator finish;
     83   iterator end_of_storage;
     84 
     85   void insert_aux(iterator position, const T& x);
     86   void deallocate() {
     87     if (start)
     88          data_allocator::deallocate(start, end_of_storage - start);
     89   }
     90 
     91   void fill_initialize(size_type n, const T& value) {
     92     start = allocate_and_fill(n, value);  // 配置空間並設初值
     93     finish = start + n;                // 調整水位
     94     end_of_storage = finish;             // 調整水位
     95   }
     96 public:
     97   iterator begin() { return start; }
     98   const_iterator begin() const { return start; }
     99   iterator end() { return finish; }
    100   const_iterator end() const { return finish; }
    101   reverse_iterator rbegin() { return reverse_iterator(end()); }
    102   const_reverse_iterator rbegin() const { 
    103     return const_reverse_iterator(end()); 
    104   }
    105   reverse_iterator rend() { return reverse_iterator(begin()); }
    106   const_reverse_iterator rend() const { 
    107     return const_reverse_iterator(begin()); 
    108   }
    109   size_type size() const { return size_type(end() - begin()); }
    110   size_type max_size() const { return size_type(-1) / sizeof(T); }
    111   size_type capacity() const { return size_type(end_of_storage - begin()); }
    112   bool empty() const { return begin() == end(); }
    113   reference operator[](size_type n) { return *(begin() + n); }
    114   const_reference operator[](size_type n) const { return *(begin() + n); }
    115 
    116   vector() : start(0), finish(0), end_of_storage(0) {}
    117   // 以下建構式,允許指定大小 n 和初值 value
    118   vector(size_type n, const T& value) { fill_initialize(n, value); }
    119   vector(int n, const T& value) { fill_initialize(n, value); }
    120   vector(long n, const T& value) { fill_initialize(n, value); }
    121   explicit vector(size_type n) { fill_initialize(n, T()); }
    122 
    123   vector(const vector<T, Alloc>& x) {
    124     start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
    125     finish = start + (x.end() - x.begin());
    126     end_of_storage = finish;
    127   }
    128 #ifdef __STL_MEMBER_TEMPLATES
    129   template <class InputIterator>
    130   vector(InputIterator first, InputIterator last) :
    131     start(0), finish(0), end_of_storage(0)
    132   {
    133     range_initialize(first, last, iterator_category(first));
    134   }
    135 #else /* __STL_MEMBER_TEMPLATES */
    136   vector(const_iterator first, const_iterator last) {
    137     size_type n = 0;
    138     distance(first, last, n);
    139     start = allocate_and_copy(n, first, last);
    140     finish = start + n;
    141     end_of_storage = finish;
    142   }
    143 #endif /* __STL_MEMBER_TEMPLATES */
    144   ~vector() { 
    145     destroy(start, finish);  // 全域函式,建構/解構基本工具。
    146     deallocate();   // 先前定義好的成員函式
    147   }
    148   vector<T, Alloc>& operator=(const vector<T, Alloc>& x);
    149   void reserve(size_type n) {
    150     if (capacity() < n) {
    151       const size_type old_size = size();
    152       iterator tmp = allocate_and_copy(n, start, finish);
    153       destroy(start, finish);
    154       deallocate();
    155       start = tmp;
    156       finish = tmp + old_size;
    157       end_of_storage = start + n;
    158     }
    159   }
    160 
    161   // 取出第一個元素內容
    162   reference front() { return *begin(); }
    163   const_reference front() const { return *begin(); }
    164   // 取出最後一個元素內容
    165   reference back() { return *(end() - 1); }
    166   const_reference back() const { return *(end() - 1); }
    167   // 增加一個元素,做為最後元素
    168   void push_back(const T& x) {
    169     if (finish != end_of_storage) {  // 還有備用空間
    170       construct(finish, x);           // 直接在備用空間中建構元素。
    171       ++finish;                              // 調整水位高度
    172     }
    173     else                                  // 已無備用空間
    174       insert_aux(end(), x);            
    175   }
    176   void swap(vector<T, Alloc>& x) {
    177     __STD::swap(start, x.start);
    178     __STD::swap(finish, x.finish);
    179     __STD::swap(end_of_storage, x.end_of_storage);
    180   }
    181   iterator insert(iterator position, const T& x) {
    182     size_type n = position - begin();
    183     if (finish != end_of_storage && position == end()) {
    184       construct(finish, x);        // 全域函式,建構/解構基本工具。
    185       ++finish;
    186     }
    187     else
    188       insert_aux(position, x);
    189     return begin() + n;
    190   }
    191   iterator insert(iterator position) { return insert(position, T()); }
    192 #ifdef __STL_MEMBER_TEMPLATES
    193   template <class InputIterator>
    194   void insert(iterator position, InputIterator first, InputIterator last){
    195     range_insert(position, first, last, iterator_category(first));
    196   }
    197 #else /* __STL_MEMBER_TEMPLATES */
    198   void insert(iterator position,
    199               const_iterator first, const_iterator last);
    200 #endif /* __STL_MEMBER_TEMPLATES */
    201 
    202   void insert (iterator pos, size_type n, const T& x);
    203   void insert (iterator pos, int n, const T& x) {
    204     insert(pos, (size_type) n, x);
    205   }
    206   void insert (iterator pos, long n, const T& x) {
    207     insert(pos, (size_type) n, x);
    208   }
    209 
    210   void pop_back() {
    211     --finish;
    212     destroy(finish);    // 全域函式,建構/解構基本工具。
    213   }
    214   // 將迭代器 position 所指之元素移除
    215   iterator erase(iterator position) {
    216     if (position + 1 != end()) // 如果 p 不是指向最後一個元素
    217       // 將 p 之後的元素一一向前遞移
    218       copy(position + 1, finish, position); 
    219 
    220     --finish;  // 調整水位
    221     destroy(finish);    // 全域函式,建構/解構基本工具。
    222     return position;
    223   }
    224   iterator erase(iterator first, iterator last) {
    225     iterator i = copy(last, finish, first);
    226     destroy(i, finish);    // 全域函式,建構/解構基本工具。
    227     finish = finish - (last - first);
    228     return first;
    229   }
    230   void resize(size_type new_size, const T& x) {
    231     if (new_size < size()) 
    232       erase(begin() + new_size, end());
    233     else
    234       insert(end(), new_size - size(), x);
    235   }
    236   void resize(size_type new_size) { resize(new_size, T()); }
    237   // 清除全部元素。注意,並未釋放空間,以備可能未來還會新加入元素。
    238   void clear() { erase(begin(), end()); }
    239 
    240 protected:
    241   iterator allocate_and_fill(size_type n, const T& x) {
    242     iterator result = data_allocator::allocate(n); // 配置n個元素空間
    243     __STL_TRY {
    244       // 全域函式,記憶體低階工具,將result所指之未初始化空間設定初值為 x,n個
    245       // 定義於 <stl_uninitialized.h>。
    246       uninitialized_fill_n(result, n, x);  
    247       return result;
    248     }
    249      // "commit or rollback" 語意:若非全部成功,就一個不留。
    250     __STL_UNWIND(data_allocator::deallocate(result, n));
    251   }
    252 
    253 #ifdef __STL_MEMBER_TEMPLATES
    254   template <class ForwardIterator>
    255   iterator allocate_and_copy(size_type n,
    256                              ForwardIterator first, ForwardIterator last) {
    257     iterator result = data_allocator::allocate(n);
    258     __STL_TRY {
    259       uninitialized_copy(first, last, result);
    260       return result;
    261     }
    262     __STL_UNWIND(data_allocator::deallocate(result, n));
    263   }
    264 #else /* __STL_MEMBER_TEMPLATES */
    265   iterator allocate_and_copy(size_type n,
    266                              const_iterator first, const_iterator last) {
    267     iterator result = data_allocator::allocate(n);
    268     __STL_TRY {
    269       uninitialized_copy(first, last, result);
    270       return result;
    271     }
    272     __STL_UNWIND(data_allocator::deallocate(result, n));
    273   }
    274 #endif /* __STL_MEMBER_TEMPLATES */
    275 
    276 
    277 #ifdef __STL_MEMBER_TEMPLATES
    278   template <class InputIterator>
    279   void range_initialize(InputIterator first, InputIterator last,
    280                         input_iterator_tag) {
    281     for ( ; first != last; ++first)
    282       push_back(*first);
    283   }
    284 
    285   // This function is only called by the constructor.  We have to worry
    286   //  about resource leaks, but not about maintaining invariants.
    287   template <class ForwardIterator>
    288   void range_initialize(ForwardIterator first, ForwardIterator last,
    289                         forward_iterator_tag) {
    290     size_type n = 0;
    291     distance(first, last, n);
    292     start = allocate_and_copy(n, first, last);
    293     finish = start + n;
    294     end_of_storage = finish;
    295   }
    296 
    297   template <class InputIterator>
    298   void range_insert(iterator pos,
    299                     InputIterator first, InputIterator last,
    300                     input_iterator_tag);
    301 
    302   template <class ForwardIterator>
    303   void range_insert(iterator pos,
    304                     ForwardIterator first, ForwardIterator last,
    305                     forward_iterator_tag);
    306 
    307 #endif /* __STL_MEMBER_TEMPLATES */
    308 };
    309 
    310 template <class T, class Alloc>
    311 inline bool operator==(const vector<T, Alloc>& x, const vector<T, Alloc>& y) {
    312   return x.size() == y.size() && equal(x.begin(), x.end(), y.begin());
    313 }
    314 
    315 template <class T, class Alloc>
    316 inline bool operator<(const vector<T, Alloc>& x, const vector<T, Alloc>& y) {
    317   return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
    318 }
    319 
    320 #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
    321 
    322 template <class T, class Alloc>
    323 inline void swap(vector<T, Alloc>& x, vector<T, Alloc>& y) {
    324   x.swap(y);
    325 }
    326 
    327 #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
    328 
    329 template <class T, class Alloc>
    330 vector<T, Alloc>& vector<T, Alloc>::operator=(const vector<T, Alloc>& x) {
    331   if (&x != this) {    // 判斷是否 self-assignment
    332     if (x.size() > capacity()) {        // 如果標的物比我本身的容量還大
    333       iterator tmp = allocate_and_copy(x.end() - x.begin(),
    334                                        x.begin(), x.end());
    335       destroy(start, finish);    // 把整個舊的vector 摧毀
    336       deallocate();            // 釋放舊空間
    337       start = tmp;                // 設定指向新空間
    338       end_of_storage = start + (x.end() - x.begin());
    339     }
    340     else if (size() >= x.size()) {    // 如果標的物大小 <= 我的大小
    341       iterator i = copy(x.begin(), x.end(), begin());
    342       destroy(i, finish);
    343     }
    344     else {
    345       copy(x.begin(), x.begin() + size(), start);
    346       uninitialized_copy(x.begin() + size(), x.end(), finish);
    347     }
    348     finish = start + x.size();
    349   }
    350   return *this;
    351 }
    352 
    353 template <class T, class Alloc>
    354 void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
    355   if (finish != end_of_storage) {  // 還有備用空間
    356     // 在備用空間起始處建構一個元素,並以vector 最後一個元素值為其初值。    
    357     construct(finish, *(finish - 1));
    358     // 調整水位。
    359     ++finish;
    360     // 以下做啥用?
    361     T x_copy = x;
    362     copy_backward(position, finish - 2, finish - 1);
    363     *position = x_copy;
    364   }
    365   else {        // 已無備用空間
    366     const size_type old_size = size();
    367     const size_type len = old_size != 0 ? 2 * old_size : 1;
    368     // 以上配置原則:如果原大小為0,則配置 1(個元素大小);
    369     // 如果原大小不為0,則配置原大小的兩倍,
    370     // 前半段用來放置原資料,後半段準備用來放置新資料。
    371 
    372     iterator new_start = data_allocator::allocate(len); // 實際配置
    373     iterator new_finish = new_start;
    374     __STL_TRY {
    375       // 將原vector 的內容拷貝到新 vector。
    376       new_finish = uninitialized_copy(start, position, new_start);
    377       // 為新元素設定初值x
    378       construct(new_finish, x);
    379       // 調整水位。
    380       ++new_finish;
    381       // 將原vector 的備用空間中的內容也忠實拷貝過來(啥用途?)
    382       new_finish = uninitialized_copy(position, finish, new_finish);
    383     }
    384 
    385 #       ifdef  __STL_USE_EXCEPTIONS 
    386     catch(...) {
    387       // "commit or rollback" 語意:若非全部成功,就一個不留。
    388       destroy(new_start, new_finish); 
    389       data_allocator::deallocate(new_start, len);
    390       throw;
    391     }
    392 #       endif /* __STL_USE_EXCEPTIONS */
    393 
    394     // 解構並釋放原 vector
    395     destroy(begin(), end());
    396     deallocate();
    397 
    398     // 調整迭代器,指向新vector
    399     start = new_start;
    400     finish = new_finish;
    401     end_of_storage = new_start + len;
    402   }
    403 }
    404 
    405 // 從 position 開始,安插 n 個元素,元素初值為 x
    406 template <class T, class Alloc>
    407 void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) {
    408   if (n != 0) { // 當 n != 0  才進行以下所有動作
    409     if (size_type(end_of_storage - finish) >= n) { 
    410       // 備用空間大於等於「新增元素個數」
    411       T x_copy = x;
    412       // 以下計算安插點之後的現有元素個數
    413       const size_type elems_after = finish - position;    
    414       iterator old_finish = finish;
    415       if (elems_after > n) { 
    416         // 「安插點之後的現有元素個數」大於「新增元素個數」
    417         uninitialized_copy(finish - n, finish, finish);
    418         finish += n;    // 將vector 尾端標記後移
    419         copy_backward(position, old_finish - n, old_finish);
    420         fill(position, position + n, x_copy);    // 從安插點開始填入新值
    421       }
    422       else {    
    423         // 「安插點之後的現有元素個數」小於等於「新增元素個數」
    424         uninitialized_fill_n(finish, n - elems_after, x_copy);
    425         finish += n - elems_after;
    426         uninitialized_copy(position, old_finish, finish);
    427         finish += elems_after;
    428         fill(position, old_finish, x_copy);
    429       }
    430     }
    431     else {
    432       // 備用空間小於「新增元素個數」(那就必須配置額外的記憶體)
    433       // 首先決定新長度:舊長度的兩倍,或舊長度+新增元素個數。
    434       const size_type old_size = size();        
    435       const size_type len = old_size + max(old_size, n);
    436       // 以下配置新的vector 空間
    437       iterator new_start = data_allocator::allocate(len);
    438       iterator new_finish = new_start;
    439       __STL_TRY {
    440         // 以下首先將舊vector 的安插點之前的元素複製到新空間。
    441         new_finish = uninitialized_copy(start, position, new_start);
    442         // 以下再將新增元素(初值皆為 n)填入新空間。
    443         new_finish = uninitialized_fill_n(new_finish, n, x);
    444         // 以下再將舊vector 的安插點之後的元素複製到新空間。
    445         new_finish = uninitialized_copy(position, finish, new_finish);
    446       }
    447 #         ifdef  __STL_USE_EXCEPTIONS 
    448       catch(...) {
    449         // 如有異常發生,實現 "commit or rollback" semantics.
    450         destroy(new_start, new_finish);
    451         data_allocator::deallocate(new_start, len);
    452         throw;
    453       }
    454 #         endif /* __STL_USE_EXCEPTIONS */
    455       // 以下清除並釋放舊的 vector 
    456       destroy(start, finish);
    457       deallocate();
    458       // 以下調整水位標記
    459       start = new_start;
    460       finish = new_finish;
    461       end_of_storage = new_start + len;
    462     }
    463   }
    464 }
    465 
    466 #ifdef __STL_MEMBER_TEMPLATES
    467 
    468 template <class T, class Alloc> template <class InputIterator>
    469 void vector<T, Alloc>::range_insert(iterator pos,
    470                                     InputIterator first, InputIterator last,
    471                                     input_iterator_tag) {
    472   for ( ; first != last; ++first) {
    473     pos = insert(pos, *first);
    474     ++pos;
    475   }
    476 }
    477 
    478 template <class T, class Alloc> template <class ForwardIterator>
    479 void vector<T, Alloc>::range_insert(iterator position,
    480                                     ForwardIterator first,
    481                                     ForwardIterator last,
    482                                     forward_iterator_tag) {
    483   if (first != last) {
    484     size_type n = 0;
    485     distance(first, last, n);
    486     if (size_type(end_of_storage - finish) >= n) {
    487       const size_type elems_after = finish - position;
    488       iterator old_finish = finish;
    489       if (elems_after > n) {
    490         uninitialized_copy(finish - n, finish, finish);
    491         finish += n;
    492         copy_backward(position, old_finish - n, old_finish);
    493         copy(first, last, position);
    494       }
    495       else {
    496         ForwardIterator mid = first;
    497         advance(mid, elems_after);
    498         uninitialized_copy(mid, last, finish);
    499         finish += n - elems_after;
    500         uninitialized_copy(position, old_finish, finish);
    501         finish += elems_after;
    502         copy(first, mid, position);
    503       }
    504     }
    505     else {
    506       const size_type old_size = size();
    507       const size_type len = old_size + max(old_size, n);
    508       iterator new_start = data_allocator::allocate(len);
    509       iterator new_finish = new_start;
    510       __STL_TRY {
    511         new_finish = uninitialized_copy(start, position, new_start);
    512         new_finish = uninitialized_copy(first, last, new_finish);
    513         new_finish = uninitialized_copy(position, finish, new_finish);
    514       }
    515 #         ifdef __STL_USE_EXCEPTIONS
    516       catch(...) {
    517         destroy(new_start, new_finish);
    518         data_allocator::deallocate(new_start, len);
    519         throw;
    520       }
    521 #         endif /* __STL_USE_EXCEPTIONS */
    522       destroy(start, finish);
    523       deallocate();
    524       start = new_start;
    525       finish = new_finish;
    526       end_of_storage = new_start + len;
    527     }
    528   }
    529 }
    530 
    531 #else /* __STL_MEMBER_TEMPLATES */
    532 
    533 template <class T, class Alloc>
    534 void vector<T, Alloc>::insert(iterator position, 
    535                               const_iterator first, 
    536                               const_iterator last) {
    537   if (first != last) {
    538     size_type n = 0;
    539     distance(first, last, n);
    540     if (size_type(end_of_storage - finish) >= n) {
    541       const size_type elems_after = finish - position;
    542       iterator old_finish = finish;
    543       if (elems_after > n) {
    544         uninitialized_copy(finish - n, finish, finish);
    545         finish += n;
    546         copy_backward(position, old_finish - n, old_finish);
    547         copy(first, last, position);
    548       }
    549       else {
    550         uninitialized_copy(first + elems_after, last, finish);
    551         finish += n - elems_after;
    552         uninitialized_copy(position, old_finish, finish);
    553         finish += elems_after;
    554         copy(first, first + elems_after, position);
    555       }
    556     }
    557     else {
    558       const size_type old_size = size();
    559       const size_type len = old_size + max(old_size, n);
    560       iterator new_start = data_allocator::allocate(len);
    561       iterator new_finish = new_start;
    562       __STL_TRY {
    563         new_finish = uninitialized_copy(start, position, new_start);
    564         new_finish = uninitialized_copy(first, last, new_finish);
    565         new_finish = uninitialized_copy(position, finish, new_finish);
    566       }
    567 #         ifdef __STL_USE_EXCEPTIONS
    568       catch(...) {
    569         destroy(new_start, new_finish);
    570         data_allocator::deallocate(new_start, len);
    571         throw;
    572       }
    573 #         endif /* __STL_USE_EXCEPTIONS */
    574       destroy(start, finish);
    575       deallocate();
    576       start = new_start;
    577       finish = new_finish;
    578       end_of_storage = new_start + len;
    579     }
    580   }
    581 }
    582 
    583 #endif /* __STL_MEMBER_TEMPLATES */
    584 
    585 #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
    586 #pragma reset woff 1174
    587 #endif
    588 
    589 __STL_END_NAMESPACE 
    590 
    591 #endif /* __SGI_STL_INTERNAL_VECTOR_H */
    592 
    593 // Local Variables:
    594 // mode:C++
    595 // End:
  • 相关阅读:
    js对象Array —— 使用.操作符和用['xxx']访问对象的区别
    JavaScript为字符串提供的一些常用方法
    ES6新增属性——,模板字符串`` 中可使用${ }代替‘+’字符串拼接
    【vue】使用vue+element搭建项目,Tree树形控件使用
    vue循环遍历List,Map,Array
    解决分页查询只能查到本页信息的问题
    vue之 js字符串驼峰和下划线互相转换
    PHP中的对象遍历技巧
    php类的复制(克隆)
    php类的自动加载
  • 原文地址:https://www.cnblogs.com/xxiaoye/p/3964307.html
Copyright © 2011-2022 走看看