myTinySTL源碼解析:vector

一、測試程序

#ifndef MYTINYSTL_VECTOR_TEST_H_
#define MYTINYSTL_VECTOR_TEST_H_

// vector test : 測試 vector 的接口與 push_back 的性能

#include <vector>

#include "../MyTinySTL/vector.h"
#include "test.h"

namespace mystl
{
namespace test
{
namespace vector_test
{

void vector_test()
{
  std::cout << "[===============================================================]\n";
  std::cout << "[----------------- Run container test : vector -----------------]\n";
  std::cout << "[-------------------------- API test ---------------------------]\n";
  int a[] = { 1,2,3,4,5 };
  mystl::vector<int> v1;
  mystl::vector<int> v2(10);
  mystl::vector<int> v3(10, 1);
  mystl::vector<int> v4(a, a + 5);
  mystl::vector<int> v5(v2);
  mystl::vector<int> v6(std::move(v2));
  mystl::vector<int> v7{ 1,2,3,4,5,6,7,8,9 };
  mystl::vector<int> v8, v9, v10;
  v8 = v3;
  v9 = std::move(v3);
  v10 = { 1,2,3,4,5,6,7,8,9 };

  FUN_AFTER(v1, v1.assign(8, 8));
  FUN_AFTER(v1, v1.assign(a, a + 5));
  FUN_AFTER(v1, v1.emplace(v1.begin(), 0));
  FUN_AFTER(v1, v1.emplace_back(6));
  FUN_AFTER(v1, v1.push_back(6));
  FUN_AFTER(v1, v1.insert(v1.end(), 7));
  FUN_AFTER(v1, v1.insert(v1.begin() + 3, 2, 3));
  FUN_AFTER(v1, v1.insert(v1.begin(), a, a + 5));
  FUN_AFTER(v1, v1.pop_back());
  FUN_AFTER(v1, v1.erase(v1.begin()));
  FUN_AFTER(v1, v1.erase(v1.begin(), v1.begin() + 2));
  FUN_AFTER(v1, v1.reverse());
  FUN_AFTER(v1, v1.swap(v4));
  FUN_VALUE(*v1.begin());
  FUN_VALUE(*(v1.end() - 1));
  FUN_VALUE(*v1.rbegin());
  FUN_VALUE(*(v1.rend() - 1));
  FUN_VALUE(v1.front());
  FUN_VALUE(v1.back());
  FUN_VALUE(v1[0]);
  FUN_VALUE(v1.at(1));
  int* p = v1.data();
  *p = 10;
  *++p = 20;
  p[1] = 30;
  std::cout << " After change v1.data() :" << "\n";
  COUT(v1);
  std::cout << std::boolalpha;
  FUN_VALUE(v1.empty());
  std::cout << std::noboolalpha;
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.max_size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.resize(10));
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.shrink_to_fit());
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.resize(6, 6));
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.shrink_to_fit());
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.clear());
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.reserve(5));
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.reserve(20));
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  FUN_AFTER(v1, v1.shrink_to_fit());
  FUN_VALUE(v1.size());
  FUN_VALUE(v1.capacity());
  PASSED;
#if PERFORMANCE_TEST_ON
  std::cout << "[--------------------- Performance Testing ---------------------]\n";
  std::cout << "|---------------------|-------------|-------------|-------------|\n";
  std::cout << "|      push_back      |";
#if LARGER_TEST_DATA_ON
  CON_TEST_P1(vector<int>, push_back, rand(), LEN1 _LL, LEN2 _LL, LEN3 _LL);
#else
  CON_TEST_P1(vector<int>, push_back, rand(), LEN1 _L, LEN2 _L, LEN3 _L);
#endif
  std::cout << "\n";
  std::cout << "|---------------------|-------------|-------------|-------------|\n";
  PASSED;
#endif
  std::cout << "[----------------- End container test : vector -----------------]\n";
}

} // namespace vector_test
} // namespace test
} // namespace mystl
#endif // !MYTINYSTL_VECTOR_TEST_H_

首先測試文件定義了兩個常用的宏:FUN_AFTER和FUN_VALUE

// 輸出容器調用函數後的結果
#define FUN_AFTER(con, fun) do {                         \
  std::string fun_name = #fun;                           \
  std::cout << " After " << fun_name << " :\n";          \
  fun;                                                   \
  COUT(con);                                             \
} while(0)
// 遍歷輸出容器
#define COUT(container) do {                             \
  std::string con_name = #container;                     \
  std::cout << " " << con_name << " :";                  \
  for (auto it : container)                              \
    std::cout << " " << it;                              \
  std::cout << "\n";                                     \
} while(0)
// 輸出容器調用函數的值
#define FUN_VALUE(fun) do {                              \
  std::string fun_name = #fun;                           \
  std::cout << " " << fun_name << " : " << fun << "\n";  \
} while(0)

還定義了一個性能測試相關的宏CON_TEST_P1

//std表示使用std定義的容器執行操作所耗費的時間
//mystl表示使用自定義的容器執行操作所耗費的時間
#define CON_TEST_P1(con, fun, arg, len1, len2, len3)         \
  TEST_LEN(len1, len2, len3, WIDE);                          \
  std::cout << "|         std         |";                    \
  FUN_TEST_FORMAT1(std::con, fun, arg, len1);                \
  FUN_TEST_FORMAT1(std::con, fun, arg, len2);                \
  FUN_TEST_FORMAT1(std::con, fun, arg, len3);                \
  std::cout << "\n|        mystl        |";                  \
  FUN_TEST_FORMAT1(mystl::con, fun, arg, len1);              \
  FUN_TEST_FORMAT1(mystl::con, fun, arg, len2);              \
  FUN_TEST_FORMAT1(mystl::con, fun, arg, len3); 

#define TEST_LEN(len1, len2, len3, wide) \
  test_len(len1, len2, len3, wide)

// 輸出測試數量級
void test_len(size_t len1, size_t len2, size_t len3, size_t wide)
{
  std::string str1, str2, str3;
  std::stringstream ss;
  ss << len1 << " " << len2 << " " << len3;
  ss >> str1 >> str2 >> str3;
  str1 += "   |";
  std::cout << std::setw(wide) << str1;
  str2 += "   |";
  std::cout << std::setw(wide) << str2;
  str3 += "   |";
  std::cout << std::setw(wide) << str3 << "\n";
}

// 常用測試性能的宏
#define FUN_TEST_FORMAT1(mode, fun, arg, count) do {         \
  srand((int)time(0));                                       \
  clock_t start, end;                                        \
  mode c;                                                    \
  char buf[10];                                              \
  start = clock();                                           \
  for (size_t i = 0; i < count; ++i)                         \
    c.fun(arg);                                              \
  end = clock();                                             \
  int n = static_cast<int>(static_cast<double>(end - start)  \
      / CLOCKS_PER_SEC * 1000);                              \
  std::snprintf(buf, sizeof(buf), "%d", n);                  \
  std::string t = buf;                                       \
  t += "ms    |";                                            \
  std::cout << std::setw(WIDE) << t;                         \
} while(0)

二、測試程序解析

2.1.初始化

2.1.1無參構造函數

vector() noexcept
  { try_init(); }
// try_init 函數,若分配失敗則忽略,不拋出異常
template <class T>
void vector<T>::try_init() noexcept//函數聲明時也不能缺少noexcept
{
  try
  {
    begin_ = data_allocator::allocate(16);//默認分配16個元素空間,begin_指向已使用空間的開頭
    end_ = begin_;//end_指向已使用空間的尾部
    cap_ = begin_ + 16;//cap_指向總分配空間的尾部,這裏+16,而不是+16*sizeof(T),因爲指針移動是依據數據類型大小
  }
  catch (...)
  {
    begin_ = nullptr;
    end_ = nullptr;
    cap_ = nullptr;
  }
}
typedef mystl::allocator<T>                      data_allocator;
template <class T>
T* allocator<T>::allocate(size_type n)//typedef size_t       size_type;
{
  if (n == 0)
    return nullptr;
  return static_cast<T*>(::operator new(n * sizeof(T)));
}

2.1.2有參構造函數

explicit vector(size_type n)
  { fill_init(n, value_type()); }

vector(size_type n, const value_type& value)
  { fill_init(n, value); }
template <class T>
void vector<T>::fill_init(size_type n, const value_type& value)
{
  const size_type init_size = mystl::max(static_cast<size_type>(16), n);
  init_space(n, init_size);
  mystl::uninitialized_fill_n(begin_, n, value);//從begin_位置填充n個value
}
template <class T>
void vector<T>::init_space(size_type size, size_type cap)
{
  try
  {
    begin_ = data_allocator::allocate(cap);
    end_ = begin_ + size;
    cap_ = begin_ + cap;
  }
  catch (...)
  {
    begin_ = nullptr;
    end_ = nullptr;
    cap_ = nullptr;
    throw;
  }
}
typedef typename allocator_type::value_type      value_type;
typedef mystl::allocator<T>                      allocator_type;
typedef T            value_type;

2.1.3迭代器範圍初始化

template <class Iter, typename std::enable_if<
    mystl::is_input_iterator<Iter>::value, int>::type = 0>
vector(Iter first, Iter last)
  {
    MYSTL_DEBUG(!(last < first));
    range_init(first, last);
  }
template <class T>
template <class Iter>
void vector<T>::range_init(Iter first, Iter last)
{
  const size_type init_size = mystl::max(static_cast<size_type>(last - first),
                                         static_cast<size_type>(16));
  init_space(static_cast<size_type>(last - first), init_size);
  mystl::uninitialized_copy(first, last, begin_);//將first至last範圍內的元素拷貝到begin_處
}

2.1.4拷貝構造函數

vector(const vector& rhs)
{
    range_init(rhs.begin_, rhs.end_);
}

2.1.5移動構造函數

vector(vector&& rhs) noexcept//僅僅是指針的賦值,並沒有申請內存,並且把右值的指針置空
    :begin_(rhs.begin_),
    end_(rhs.end_),
    cap_(rhs.cap_)
{
    rhs.begin_ = nullptr;
    rhs.end_ = nullptr;
    rhs.cap_ = nullptr;
}

2.1.6初始化列表初始化

vector(std::initializer_list<value_type> ilist)
{
    range_init(ilist.begin(), ilist.end());
}

2.2賦值

2.2.1常引用賦值

template <class T>
vector<T>& vector<T>::operator=(const vector& rhs)
{
  if (this != &rhs)//不處理自己給自己賦值
  {
    const auto len = rhs.size();
    if (len > capacity())//當前vector容量不夠,就需要擴容
    { 
      vector tmp(rhs.begin(), rhs.end());
      swap(tmp);//this和另一個vector互換
    }
    else if (size() >= len)//當前vector內容比rhs長,需要將當前vector多餘的內容銷燬
    {
      auto i = mystl::copy(rhs.begin(), rhs.end(), begin());
      data_allocator::destroy(i, end_);//刪除範圍內的元素,但並不會釋放空間
      end_ = begin_ + len;
    }
    else
    { 
      mystl::copy(rhs.begin(), rhs.begin() + size(), begin_);
      mystl::uninitialized_copy(rhs.begin() + size(), rhs.end(), end_);//將多餘部分置零
      cap_ = end_ = begin_ + len;
    }
  }
  return *this;
}

2.2.2移動賦值

template <class T>
vector<T>& vector<T>::operator=(vector&& rhs) noexcept
{
  destroy_and_recover(begin_, end_, cap_ - begin_);
  begin_ = rhs.begin_;
  end_ = rhs.end_;
  cap_ = rhs.cap_;
  rhs.begin_ = nullptr;
  rhs.end_ = nullptr;
  rhs.cap_ = nullptr;
  return *this;
}
template <class T>
void vector<T>::
destroy_and_recover(iterator first, iterator last, size_type n)
{
  data_allocator::destroy(first, last);//刪除範圍內的元素,但並不會釋放空間
  data_allocator::deallocate(first, n);//釋放空間
}

2.2.3初始化列表賦值

vector& operator=(std::initializer_list<value_type> ilist)
{
    vector tmp(ilist.begin(), ilist.end());
    swap(tmp);
    return *this;
}

2.3析構函數

~vector()
{ 
    destroy_and_recover(begin_, end_, cap_ - begin_);
    begin_ = end_ = cap_ = nullptr;
}

三、常用函數

3.1迭代器相關函數

  iterator               begin()         noexcept 
  { return begin_; }
  const_iterator         begin()   const noexcept
  { return begin_; }
  iterator               end()           noexcept
  { return end_; }
  const_iterator         end()     const noexcept 
  { return end_; }

  reverse_iterator       rbegin()        noexcept 
  { return reverse_iterator(end()); }
  const_reverse_iterator rbegin()  const noexcept
  { return const_reverse_iterator(end()); }
  reverse_iterator       rend()          noexcept
  { return reverse_iterator(begin()); }
  const_reverse_iterator rend()    const noexcept 
  { return const_reverse_iterator(begin()); }
  typedef mystl::reverse_iterator<iterator>        reverse_iterator;
  typedef mystl::reverse_iterator<const_iterator>  const_reverse_iterator;

  const_iterator         cbegin()  const noexcept 
  { return begin(); }
  const_iterator         cend()    const noexcept
  { return end(); }
  const_reverse_iterator crbegin() const noexcept
  { return rbegin(); }
  const_reverse_iterator crend()   const noexcept
  { return rend(); }

3.2容量相關函數

bool      empty()    const noexcept
{ return begin_ == end_; }
size_type size()     const noexcept
{ return static_cast<size_type>(end_ - begin_); }
size_type max_size() const noexcept
{ return static_cast<size_type>(-1) / sizeof(T); }
size_type capacity() const noexcept
{ return static_cast<size_type>(cap_ - begin_); }
// 預留空間大小,當原容量小於要求大小時,纔會重新分配
template <class T>
void vector<T>::reserve(size_type n)
{
  if (capacity() < n)
  {
    THROW_LENGTH_ERROR_IF(n > max_size(),
                          "n can not larger than max_size() in vector<T>::reserve(n)");
    const auto old_size = size();
    auto tmp = data_allocator::allocate(n);
    mystl::uninitialized_move(begin_, end_, tmp);
    data_allocator::deallocate(begin_, cap_ - begin_);
    begin_ = tmp;
    end_ = tmp + old_size;
    cap_ = begin_ + n;
  }
}
// 放棄多餘的容量
template <class T>
void vector<T>::shrink_to_fit()
{
  if (end_ < cap_)
  {
    reinsert(size());
  }
}
template <class T>
void vector<T>::reinsert(size_type size)
{
  auto new_begin = data_allocator::allocate(size);
  try
  {
    mystl::uninitialized_move(begin_, end_, new_begin);
  }
  catch (...)
  {
    data_allocator::deallocate(new_begin, size);
    throw;
  }
  data_allocator::deallocate(begin_, cap_ - begin_);
  begin_ = new_begin;
  end_ = begin_ + size;
  cap_ = begin_ + size;
}

3.3元素訪問相關函數

reference operator[](size_type n)
{
MYSTL_DEBUG(n < size());
return *(begin_ + n);
}
typedef typename allocator_type::reference       reference;
typedef mystl::allocator<T>                      allocator_type;
typedef T&           reference;
const_reference operator[](size_type n) const
{
MYSTL_DEBUG(n < size());
return *(begin_ + n);
}
reference at(size_type n)
{
THROW_OUT_OF_RANGE_IF(!(n < size()), "vector<T>::at() subscript out of range");
return (*this)[n];
}
const_reference at(size_type n) const
{
THROW_OUT_OF_RANGE_IF(!(n < size()), "vector<T>::at() subscript out of range");
return (*this)[n];
}

reference front()
{
MYSTL_DEBUG(!empty());
return *begin_;
}
const_reference front() const
{
MYSTL_DEBUG(!empty());
return *begin_;
}
reference back()
{
MYSTL_DEBUG(!empty());
return *(end_ - 1);
}
const_reference back() const
{
MYSTL_DEBUG(!empty());
return *(end_ - 1);
}

pointer       data()       noexcept { return begin_; }
typedef typename allocator_type::pointer         pointer;
typedef mystl::allocator<T>                      allocator_type;
typedef T*           pointer;
const_pointer data() const noexcept { return begin_; }

3.4修改容器相關函數

// assign將vector賦值爲n個value
void assign(size_type n, const value_type& value)
{ fill_assign(n, value); }
template <class T>
void vector<T>::fill_assign(size_type n, const value_type& value)
{
  if (n > capacity())
  {
    vector tmp(n, value);
    swap(tmp);
  }
  else if (n > size())
  {
    mystl::fill(begin(), end(), value);
    end_ = mystl::uninitialized_fill_n(end_, n - size(), value);
  }
  else
  {
    //刪除末尾多出來的內容
    erase(mystl::fill_n(begin_, n, value), end_);
  }
}
template <class Iter, typename std::enable_if<
    mystl::is_input_iterator<Iter>::value, int>::type = 0>
  void assign(Iter first, Iter last)
{
  MYSTL_DEBUG(!(last < first));
  copy_assign(first, last, iterator_category(first));
}
template <class T>
template <class IIter>
void vector<T>::copy_assign(IIter first, IIter last, input_iterator_tag)
{
  auto cur = begin_;
  for (; first != last && cur != end_; ++first, ++cur)
  {
    *cur = *first;
  }
  if (first == last)//如果size比迭代器範圍大,說明後面還又內容,應該刪掉
  {
    erase(cur, end_);
  }
  else//繼續複製迭代器範圍內的元素
  {
    insert(end_, first, last);
  }
}
void assign(std::initializer_list<value_type> il)
{ copy_assign(il.begin(), il.end(), mystl::forward_iterator_tag{}); }

// emplace / emplace_back
// 在 pos 位置就地構造元素,避免額外的複製或移動開銷
//將參數傳遞給T的構造函數,而不是包含參數的對象,emplace使用這些參數在內存空間中直接構造元素
template <class T>
template <class ...Args>
typename vector<T>::iterator
vector<T>::emplace(const_iterator pos, Args&& ...args)
{
  MYSTL_DEBUG(pos >= begin() && pos <= end());
  iterator xpos = const_cast<iterator>(pos);//去const
  const size_type n = xpos - begin_;
  if (end_ != cap_ && xpos == end_)//在vecotr末尾插入,相當於emplace_back
  {
    data_allocator::construct(mystl::address_of(*end_), mystl::forward<Args>(args)...);
    ++end_;
  }
  else if (end_ != cap_)//還有未使用空間
  {
    auto new_end = end_;
    data_allocator::construct(mystl::address_of(*end_), *(end_ - 1));
    ++new_end;
    mystl::copy_backward(xpos, end_ - 1, end_);//emplace同樣有數據搬運的操作
    *xpos = value_type(mystl::forward<Args>(args)...);
  }
  else//如果空間不夠則重新分配空間,並在 pos 處就地構造元素
  {
    reallocate_emplace(xpos, mystl::forward<Args>(args)...);
  }
  return begin() + n;
}

// 在尾部就地構造元素,避免額外的複製或移動開銷
template <class T>
template <class ...Args>
void vector<T>::emplace_back(Args&& ...args)
{
  if (end_ < cap_)
  {
    data_allocator::construct(mystl::address_of(*end_), mystl::forward<Args>(args)...);
    ++end_;
  }
  else
  {
    reallocate_emplace(end_, mystl::forward<Args>(args)...);
  }
}

// push_back / pop_back
// 在尾部插入元素
template <class T>
void vector<T>::push_back(const value_type& value)
{
  if (end_ != cap_)
  {
    data_allocator::construct(mystl::address_of(*end_), value);
    ++end_;
  }
  else
  {
    reallocate_insert(end_, value);
  }
}
void push_back(value_type&& value)
{ emplace_back(mystl::move(value)); }

// 彈出尾部元素
template <class T>
void vector<T>::pop_back()
{
  MYSTL_DEBUG(!empty());
  data_allocator::destroy(end_ - 1);
  --end_;
}
// emplace和insert相比,只是因爲傳參而不是傳對象,所以少了構建一次臨時對象的步驟
template <class T>
typename vector<T>::iterator vector<T>::insert(const_iterator pos, const value_type& value)
{
  MYSTL_DEBUG(pos >= begin() && pos <= end());
  iterator xpos = const_cast<iterator>(pos);
  const size_type n = pos - begin_;
  if (end_ != cap_ && xpos == end_)
  {
    data_allocator::construct(mystl::address_of(*end_), value);
    ++end_;
  }
  else if (end_ != cap_)
  {
    auto new_end = end_;
    data_allocator::construct(mystl::address_of(*end_), *(end_ - 1));
    ++new_end;
    auto value_copy = value;  // 避免元素因以下複製操作而被改變
    mystl::copy_backward(xpos, end_ - 1, end_);
    *xpos = mystl::move(value_copy);
  }
  else
  {
    reallocate_insert(xpos, value);
  }
  return begin_ + n;
}
iterator insert(const_iterator pos, value_type&& value)
{ return emplace(pos, mystl::move(value)); }

iterator insert(const_iterator pos, size_type n, const value_type& value)
{
MYSTL_DEBUG(pos >= begin() && pos <= end());
return fill_insert(const_cast<iterator>(pos), n, value);
}

template <class Iter, typename std::enable_if<
mystl::is_input_iterator<Iter>::value, int>::type = 0>
void     insert(const_iterator pos, Iter first, Iter last)
{
MYSTL_DEBUG(pos >= begin() && pos <= end() && !(last < first));
copy_insert(const_cast<iterator>(pos), first, last);
}

// erase / clear
template <class T>
typename vector<T>::iterator
vector<T>::erase(const_iterator pos)
{
  MYSTL_DEBUG(pos >= begin() && pos < end());
  iterator xpos = begin_ + (pos - begin());
  mystl::move(xpos + 1, end_, xpos);
  data_allocator::destroy(end_ - 1);
  --end_;
  return xpos;
}
template <class T>
typename vector<T>::iterator
vector<T>::erase(const_iterator first, const_iterator last)
{
  MYSTL_DEBUG(first >= begin() && last <= end() && !(last < first));
  const auto n = first - begin();
  iterator r = begin_ + (first - begin());
  data_allocator::destroy(mystl::move(r + (last - first), end_, r), end_);
  end_ = end_ - (last - first);
  return begin_ + n;
}
void     clear() { erase(begin(), end()); }

// resize / reverse
void     resize(size_type new_size) { return resize(new_size, value_type()); }
template <class T>
void vector<T>::resize(size_type new_size, const value_type& value)
{
  if (new_size < size())//截斷
  {
    erase(begin() + new_size, end());
  }
  else
  {
    insert(end(), new_size - size(), value);
  }
}

void     reverse() { mystl::reverse(begin(), end()); }

// swap
template <class T>
void vector<T>::swap(vector<T>& rhs) noexcept
{
  if (this != &rhs)
  {
    mystl::swap(begin_, rhs.begin_);
    mystl::swap(end_, rhs.end_);
    mystl::swap(cap_, rhs.cap_);
  }
}

3.5重載比較運算符

template <class T>
bool operator==(const vector<T>& lhs, const vector<T>& rhs)
{
  return lhs.size() == rhs.size() &&
    mystl::equal(lhs.begin(), lhs.end(), rhs.begin());
}

template <class T>
bool operator<(const vector<T>& lhs, const vector<T>& rhs)
{
  return mystl::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), lhs.end());
}

template <class T>
bool operator!=(const vector<T>& lhs, const vector<T>& rhs)
{
  return !(lhs == rhs);
}

template <class T>
bool operator>(const vector<T>& lhs, const vector<T>& rhs)
{
  return rhs < lhs;
}

template <class T>
bool operator<=(const vector<T>& lhs, const vector<T>& rhs)
{
  return !(rhs < lhs);
}

template <class T>
bool operator>=(const vector<T>& lhs, const vector<T>& rhs)
{
  return !(lhs < rhs);
}

// 重載 mystl 的 swap
template <class T>
void swap(vector<T>& lhs, vector<T>& rhs)
{
  lhs.swap(rhs);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章