MyTinySTL的vector源码分析

mystl项目地址为:https://github.com/Alinshans/MyTinySTL

原STL库十分庞大,父子类关系十分复杂。故借这个项目来研究原STL库的代码逻辑,对代码的理解都以注释的形式写在了注释里

vector

vector对象下边有三个迭代器

  iterator begin_;  // 表示目前使用空间的头部
  iterator end_;    // 表示目前使用空间的尾部
  iterator cap_;    // 表示目前储存空间的尾部

vector的构造一般借助其他函数进行构造,例如try_init(),fill_init(),range_init();

// try_init 函数,若分配失败则忽略,不抛出异常
template <class T>
void vector<T>::try_init() noexcept
{
  try{
    begin_ = data_allocator::allocate(16);/*初始化了一个大小为16的空间,在allocate中,使用函数模板初始化单个元素空间大小,接触sizeof()函数计算大小*/
    end_ = begin_;
    cap_ = begin_ + 16;
  }
  ......
}
// fill_init 函数
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);/*最小空间为16,此时init_size为cap大小*/
  init_space(n, init_size);//分配空间,初始化指针
  mystl::uninitialized_fill_n(begin_, n, value);/*uninitialized_fill_n()->unchecked_uninit_fill_n()->fill_n()->unchecked_fill_n()->memset(),该函数调用其他函数,最终底层为memset()*/
}
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_);/*uninitialized_copy()->unchecked_uninit_copy()->copy()->unchecked_copy()->memmove(),底层是一个menmove()函数*/
}

对于参数是其他vector,数组等的有参构造,基本是调用上述函数,函数的参数为迭代器的起点和终点:例如

vector tmp(ilist.begin(), ilist.end());//起点和终点
range_init(ilist.begin(), ilist.end());

析构函数:

~vector()
{ 
    destroy_and_recover(begin_, end_, cap_ - begin_);
    begin_ = end_ = cap_ = nullptr;
}
destroy_and_recover(iterator first, iterator last, size_type n)
{
    data_allocator::destroy(first, last); //底层原理是调用destroy_one()一个一个元素析构
    data_alloccator::deallocate(first, n);//底层使用delete
}

vector的迭代器就是指针

 typedef value_type*                iterator;
 //对于begin()等的函数,直接返回指针
 iterator end() noexcept
  { return end_; }

vector中重载了"[]",在at函数中,使用了此重载

  reference at(size_type n)
  		/*返回引用:当函数返回引用类型的时候,没有复制返回值,而是返回对象的引用(即对象本身)。*/
  {
    THROW_OUT_OF_RANGE_IF(!(n < size()), "vector<T>::at() subscript out of range");
    return (*this)[n];
  } 

assign()函数,底层有两种形式

fill_assign();
copy_assign(IIter first, IIter last, input_iterator_tag)
{
	......
	*cur = *first;//就是一个一个复制
	......
	insert();
}
vector<T>::insert(const_iterator pos, const value_type& value)
{
  ......
  if (end_ != cap_ && xpos == end_){
    data_allocator::construct(mystl::address_of(*end_), value);
    ++end_;
  }/*如果空间满了但插入元素是最后一个元素,则新new一个空间放入元素(底层代码有new,且只是一个空间)*/
  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);
  }/*空间没满则插入点后面元素移动一个位置(调用copy_backward),然后插入(调用move函数)*/ 
  else{
    reallocate_insert(xpos, value);
  }/*否则重新分配空间并在 pos 处插入元素*/
  return begin_ + n;
}

emplace()在 vector 容器指定位置之前插入一个新的元素 ,insert是指定位置
那么谁的运行效率更高呢?答案是 emplace()。
原因:通过 insert() 函数向 vector 容器中插入类对象,需要调用类的构造函数和移动构造函数(或拷贝构造函数);而通过 emplace() 函数实现同样的功能,只需要调用构造函数即可。

pushback()函数插入元素,pop_back()删除元素

void push_back(value_type&& value)
{ emplace_back(mystl::move(value)); } //move底层为memmove
void vector<T>::emplace_back(Args&& ...args)//emplace_back
{
  if (end_ < cap_){//新分配一个空间,调用构造函数
    data_allocator::construct(mystl::address_of(*end_), mystl::forward<Args>(args)...);
      std::forward来恢复模板参数的左值或右值属性
    ++end_;
  }
  else{
    reallocate_emplace(end_, mystl::forward<Args>(args)...);
  }
}
// 弹出尾部元素
template <class T>
void vector<T>::pop_back()
{
  MYSTL_DEBUG(!empty());
  data_allocator::destroy(end_ - 1); destroy 将对象析构
  --end_;
}

复制赋值操作符

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);
    }
    else if (size() >= len){//原空间较大则复制到原空间,然后释放多余的空间
      auto i = mystl::copy(rhs.begin(), rhs.end(), begin());
      data_allocator::destroy(i, end_);
      end_ = begin_ + len;
    }
    ......
  }
  return *this;
}
// 删除 pos 位置上的元素
template <class T>
typename vector<T>::iterator
vector<T>::erase(const_iterator pos)
{
  MYSTL_DEBUG(pos >= begin() && pos < end());
  iterator xpos = begin_ + (pos - begin());//xpos的位置
  mystl::move(xpos + 1, end_, xpos);//将xpos后面的元素前移,覆盖xpos的元素
  data_allocator::destroy(end_ - 1);//那么最后一个元素是多余的,析构掉
  --end_;
  return xpos;
}
//对于删除区间上的元素,基本逻辑是:前移n个元素,然后析构中间的n个元素

重载比较操作符

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());
}
bool lexicographical_compare(const unsigned char* first1,
                             const unsigned char* last1,
                             const unsigned char* first2,
                             const unsigned char* last2)
{
  const auto len1 = last1 - first1;
  const auto len2 = last2 - first2;
  // 先比较相同长度的部分
  const auto result = std::memcmp(first1, first2, mystl::min(len1, len2));
  // 若相等,长度较长的比较大
  return result != 0 ? result < 0 : len1 < len2;
}
//C 库函数 int memcmp(const void *str1, const void *str2, size_t n)) 把存储区 str1 和存储区 str2 的前 n 个字节进行比较。

此源码有很多相似的函数已经分析,对于逻辑简单的就跳过了。

本文仅供学习。
如有错误,请指出。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值