C++标准库学习之 容器选择概览

161 阅读3分钟

在这篇文章之前的关于C++的文章都是介绍容器与容器内的一些简单的应用,并没有说明在什么样的场景下我们该选择什么样的容器,以及容器内保存什么的数据,是数据还是 pointer,都没有介绍,从这篇文章开始就要针对各个容器详细的看看他的使用案例

在介绍各个容器前需要了解 STL 容器共通的能力

1: 所有的容器STL 容器保存都是value ,而并非一个 reference ,容器进行插入动作时实际上进行copy或者move操作, 举个例子,vector vec 中每次push_back实际上是将 Persoon copy 到vector 容器内,并不是保存的Person的引用,想要实现这种情况有两种选择 wrapper 或者 使用智能指针

std::vector<std::shared_ptr<Person >> ttt;

在每次都进行拷贝构造的情况,如果你的 value 比较复杂,拷贝需要消耗的性能比较大,你就要考虑使用 move 操作了,

2:元素在容器内必须以特性顺序保存,并且可以使用迭代器访问到每一个元素,在使用迭代器进行多次遍历的过程中访问的是相同的路径,

3:一般而言各项操作都并非是绝对安全的,在进行数据操作前必须要保证操作函数的实参是具有意义的,违反条件可能会导致不明确行为,例子如下

   vector<int> vec1={1,2,3};
   vector<int> vec2;
   
    std::copy(vec1.begin(),vec1.end(),vec2.begin());
    

由于使用的是 copy 并非insert ,并且vec2并没有实际分配内存的情况下,使用copy 就会将数据分配到野指针上面导致出现问题,

STL 共通操作

初始化 Initialization

每一个容器都提供了一个default 函数 和析构函数 ,以vector 为例

// 空构造函数
vector()
#if __cplusplus >= 201103L
      noexcept(is_nothrow_default_constructible<_Alloc>::value)
#endif
      : _Base() { }
    
//析构函数   
~vector() _GLIBCXX_NOEXCEPT
{ std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
  _M_get_Tp_allocator()); }

    

// 拷贝构造函数
   vector(const vector& __x)
   : _Base(__x.size(),
     _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
   { this->_M_impl._M_finish =
std::__uninitialized_copy_a(__x.begin(), __x.end(),
         this->_M_impl._M_start,
         _M_get_Tp_allocator());
   }

再来看看常用用法都属于哪种类型,我们以 list 双向链表为例

//Initialization Type 构造,C++ 11 引入的
std::list<string> lis={"2","2","4"};    
    
// 使用default 空构造函数,创建一个不含有任何元素的容器
std::list<Person> list;
    
// 使用拷贝构造函数,重新建立一个新的容器,将 list 中所有的元素 copy 到新的容器内   
std::list<Person> l2(list);

// 使用拷贝构造函数,重新建立一个新的容器,将 list 中所有的元素 copy 到新的容器内     
std::list<Person> l2=list;    

//move     
std::list<Person> l3(std::make_move_iterator(lis.begin()),std::make_move_iterator(lis.end()));

//从开始节点copy到结尾节点    
std::list<Person> l4(lis.cbegin(),lis.cend());    

//交换两个容器内的数据
ll1.swap(ll2);    
    
// 获取数据量大小    
lis.size();    

//是否为空    
lis.empty(); 

// 两个容器相等,    
lis==list
    
从代码可以看出来两个容器相等的条件是数据类型相同,元素个数相同,切每个节点的元素相等    
template<typename _Tp, typename _Alloc>
    inline bool
    operator==(const list<_Tp, _Alloc>& __x, const list<_Tp, _Alloc>& __y)
    {
#if _GLIBCXX_USE_CXX11_ABI
      if (__x.size() != __y.size())
   return false;
#endif

      typedef typename list<_Tp, _Alloc>::const_iterator const_iterator;
      const_iterator __end1 = __x.end();
      const_iterator __end2 = __y.end();

      const_iterator __i1 = __x.begin();
      const_iterator __i2 = __y.begin();
      while (__i1 != __end1 && __i2 != __end2 && *__i1 == *__i2)
   {
     ++__i1;
     ++__i2;
   }
      return __i1 == __end1 && __i2 == __end2;
    }