10 STL -- 学习笔记

零碎知识:

hypot()函数

Mindhacks.cn博客

c++的多态是用函数指针来实现的,可以网上查看具体如何实现

STL六大组件:

  • 容器 装数据的数据结构
    • 序列式容器 vector,deque,list
    • 关联式容器
    • 无序关联式容器
  • 迭代器 广义的指针
  • 适配器 适配功能
  • 算法 操作数据
  • 函数对象(仿函数)
  • 空间配置器allocator 空间的申请和释放

一.容器

存放元素(数据)的数据结构
请添加图片描述

1. 序列式容器
1.vector[数组]

请添加图片描述
构造函数:

//无参:
vector();
vector() noexcept(noexcept(Allocator()));
constexpr vector() noexcept(noexcept(Allocator()));

explicit vector( const Allocator& alloc );
explicit vector( const Allocator& alloc ) noexcept;
constexpr explicit vector( const Allocator& alloc ) noexcept;

//count个value对象,没写就默认为0:
explicit vector( size_type count,
                 const T& value = T(),
                 const Allocator& alloc = Allocator());
vector( size_type count,
        const T& value,
        onst Allocator& alloc = Allocator());
constexpr vector( size_type count,
                  const T& value,
                  const Allocator& alloc = Allocator());

explicit vector( size_type count );
explicit vector( size_type count, const Allocator& alloc = Allocator() );
constexpr explicit vector( size_type count,
                           const Allocator& alloc = Allocator() );    

//迭代器范围:
template< class InputIt >
vector( InputIt first, InputIt last, const Allocator& alloc = Allocator() );

template< class InputIt >
constexpr vector( InputIt first, InputIt last, const Allocator& alloc = Allocator() );

//拷贝构造和移动构造:
vector( const vector& other );
constexpr vector( const vector& other );

vector( const vector& other, const Allocator& alloc );
constexpr vector( const vector& other, const Allocator& alloc );

vector( vector&& other );
vector( vector&& other ) noexcept;
constexpr vector( vector&& other ) noexcept;
vector( vector&& other, const Allocator& alloc );
constexpr vector( vector&& other, const Allocator& alloc );
   
//等价于大括号:
vector( std::initializer_list<T> init,
        const Allocator& alloc = Allocator() );

constexpr vector( std::initializer_list<T> init,
                  const Allocator& alloc = Allocator() );
//4种初始化
vector<int> number;
vector<int> numbers(5, 10);
int arr[5] = {1, 2, 3, 4, 5};
vector<int> numbers(arr, arr + 5);
vector<int> numbers{1, 2, 3, 4, 5};

四种遍历方式:

  1. for循环+下标

  2. for+迭代器:vector<int>::const_iterator cit = number.begin()

  3. for+广义指针:auto it = number.begin()

  4. for(auto &elem : number)

修改:

  • push_back():有左值引用和右值引用

  • pop_back() : 这两个返回值类型都是void

  • insert():

      找到一个位置,插入一个value

      找到一个位置,插入count个value

      找到一个位置,插入迭代器范围

      找到一个位置,插入大括号

    terator insert( iterator pos, const T& value );
    iterator insert( const_iterator pos, const T& value );
    constexpr iterator insert( const_iterator pos, const T& value );
    iterator insert( const_iterator pos, T&& value );
    constexpr iterator insert( const_iterator pos, T&& value );
    
    void insert( iterator pos, size_type count, const T& value );
    iterator insert( const_iterator pos, size_type count, const T& value );
    constexpr iterator insert( const_iterator pos, size_type count, const T& value );
    
    template< class InputIt >
    void insert( iterator pos, InputIt first, InputIt last );
    
    template< class InputIt >
    iterator insert( const_iterator pos,
                     InputIt first, InputIt last );
    template< class InputIt >
    constexpr iterator insert( const_iterator pos,
                               InputIt first, InputIt last );
    
    iterator insert( const_iterator pos, std::initializer_list<T> ilist );
    constexpr iterator insert( const_iterator pos,
                               std::initializer_list<T> ilist );
    

    Insert()的扩容机制:

      push_back之所以可以进行2倍扩容, 是因为每次只是插入一个元素,所以两倍就不会造成二次扩容。对于insert而言,插入元素个数不确定的,所以两倍扩容不合适 ,size() = n , capacity() = m,insert元素的个数为t

      1、t <= m - n,此时不会扩容

      2、m - n < t, t < n, 按照2 * n扩容

      3、m - n < t, n < t < m, 按照n + t扩容

      4、m - n < t, t > m, 按照n + t扩容

  • erase():删除连续某个值时,会存在bug,要注意迭代器++的时机,因为erase某个元素后,后面的元素会往前移,然后此时++,跳过了中间的一个元素。

  • clear()清空元素,但不改变容量大小;

  • shrink_to_fit()缩减掉多余未用的空间,常与clear()连用。

  • emplace_back()可传某个对象的构造函数的参数,少了一次构造,直接在内部创建创建对象加入了vector。如果是push_back()传入某个对象的构造函数的参数,先构造临时对象,然后再拷贝构造一次,将拷贝的对象加入vector。

容量获取:

  size()

  capacity()

vector的迭代器失效问题:

  Insert()多个数据发生扩容后,再insert()会core dump的原因:用的迭代器标志要插入的位置,而vector扩容是重新申请一片新的空间,把老空间的数据拷贝过去,此时迭代器仍然指的是旧空间中的位置,此时在此处进行插入,当然会出错。要正常插入,就要把迭代器重新置位。

vector底层原理:三个指针
请添加图片描述

访问第一个元素的地址:

&number;
&number[0];
&*number.begin();
int *pdata = number.begin()

at()和下标访问[ ]的区别:at更安全,有越界检查。

push_back()两倍扩容:const size_type __len = (__old_size != 0) ? 2 * __old_size : 1;

萃取技术(类型萃取):迭代器相关

(看序列式容器的迭代器的源码,有些是类内部实现的指针,有些是外部重载了*、->的类的吸收进来…)

//对于vector来说,萃取出的一些类型:
private:
  typedef _Vector_base<_Tp, _Alloc> _Base;
public:
  typedef _Tp value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

//对于deque来说,萃取的一些类型,来自于它继承的类:
typedef _Deque_base<_Tp, _Alloc> _Base;
public: 
  typedef _Tp value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
public:                         // Iterators
  typedef typename _Base::iterator       iterator;
  typedef typename _Base::const_iterator const_iterator;

vector不能进行头部插入和删除的原因:物理上面是连续的,而deque和list不是,要在头部插入和删除时要移动元素,时间复杂度O(N)。

2. deque[双端队列]

构造函数:一模一样,同vector

遍历:同上,一模一样

修改:同vector,另外还有:

  push_front()

  pop_front() 返回值类型都是void

  Insert():插入一个值,插入count个值,插入迭代器范围,插入大括号,使用同vector的一样。但是在底层原理上,插入时,在队列前一半和后一半插入时效果截然不同,涉及移动前一半或者后一半。

  clear()清空元素;

  shrink_to_fit()缩减掉多余未用的空间;

  emplace_back()/emplace_front()可传某个对象的构造函数的参数,少了一次构造,直接在内部创建创建对象加入了deque。

容量获取:

  size()

  无capacity()

底层实现(通过中控器数组来管理,逻辑上连续,物理上分散):
请添加图片描述
请添加图片描述

3.list[双向链表]

构造函数:一模一样,同vector;

遍历:没重载下标访问运算符,不能通过下标访问。

修改:同vector,另外还有:

  push_front()

  pop_front() 返回值类型都是void;

  Insert():插入一个值,插入count个值,插入迭代器范围,插入大括号,具体同vector的一样。

  clear()清空元素;

  emplace_back()可传某个对象的构造函数的参数,少了一次构造,直接在内部创建对象加入了列表。如果是push_back()传入某个对象的构造函数的参数,先构造临时对象,然后再拷贝构造一次,将拷贝的对象加入列表。

  数据结构决定,无shrink_to_fit()

容量获取:

  size()

  无capacity()

独有函数:

  sort(),带参数的sort传入的参数是一个对象,该对象的类重载了()运算符,比如std::less<int>(),std::greater<int>(),可以自己来写特定的,使用方式:

void sort();
template<class Compare>
void sort(Compare comp);
//函数对象(仿函数)
template <typename  T>
struct CompareList
{
    bool operator()(const T &lhs, const T &rhs) const
    {
        cout << "bool operator()(const T &, const T &)" << endl;
        return lhs < rhs;
    }
};
void test()
{
	number.sort();
    number.sort(std::less<int>()); 
    number.sort(std::greater<int>()); 
    number.sort(CompareList<int>()); 
}

  reverse()

  unique(),只能作用于连续的相同元素。

  merge(),会把参数里的list的元素移到调用的list里,且当两个有序list merge后仍然有序。

void merge( list& other );
void merge( list&& other );

template <class Compare>
void merge( list& other, Compare comp );

template <class Compare>
void merge( list&& other, Compare comp );

  splice(),在两个链表里将元素进行挪动(挪动后被挪动的链表里的响应元素不存在了),当在同一个链表里进行挪动时,注意不要发生元素的交叉。

void splice( const_iterator pos, list& other );
void splice( const_iterator pos, list&& other );

void splice( const_iterator pos, list& other, const_iterator it );
void splice( const_iterator pos, list&& other, const_iterator it );

void splice( const_iterator pos, list& other,
             const_iterator first, const_iterator last);
void splice( const_iterator pos, list&& other,
             const_iterator first, const_iterator last );
4.array
5.forward_list

阅读string的源码,看看string的扩容机制。

2. 关联式容器
1. set
template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;

特点:

  1、关键字必须唯一,不能重复

  2、默认情况下,会按照关键字进行升序排列

  3、set底层使用的是红黑树

构造函数:

//空、迭代器范围、拷贝构造、移动构造、大括号
set();
explicit set( const Compare& comp,const Allocator& alloc = Allocator() );
explicit set( const Allocator& alloc );

template< class InputIt >
set( InputIt first, InputIt last,
     const Compare& comp = Compare(),
     const Allocator& alloc = Allocator() );

template< class InputIt >
set( InputIt first, InputIt last, const Allocator& alloc)
    : set(first, last, Compare(), alloc) {}

set( const set& other );
set( const set& other, const Allocator& alloc );
set( set&& other );
set( set&& other, const Allocator& alloc );

set( std::initializer_list<value_type> init,
     const Compare& comp = Compare(),
     const Allocator& alloc = Allocator() );

set( std::initializer_list<value_type> init, const Allocator& alloc )
    : set(init, Compare(), alloc) {}

访问:

count()

find():注意返回值

iterator find( const Key& key );
const_iterator find( const Key& key ) const;

template< class K > iterator find( const K& x );

template< class K > const_iterator find( const K& x ) const;

不支持下标访问和修改操作;

lower_bound():返回一个不小于传进去的参数key的第一个值对应的迭代器;

upper_bound():返回一个大于传进去的参数key的第一个值对应的迭代器;

equal_range():返回的是一个pair类型,其中包含两个迭代器,是与传入参数equal的一个range。

插入:

Insert():注意返回值,插入单个值,插入迭代器范围,插入大括号;

std::pair<iterator,bool> insert( const value_type& value );
std::pair<iterator,bool> insert( value_type&& value );

iterator insert( iterator hint, const value_type& value );
iterator insert( const_iterator hint, const value_type& value );
iterator insert( const_iterator hint, value_type&& value );
    
template< class InputIt >
void insert( InputIt first, InputIt last );

void insert( std::initializer_list<value_type> ilist );

insert_return_type insert( node_type&& nh );
iterator insert( const_iterator hint, node_type&& nh );

当set中存储的是自定义类型时,该自定义类型要重载符号小于号(使用std::less时),重载符号大于号(使用std::greater时),不然插入红黑树时无法比较。或者定义时set<int,CompareSet>,传入自己所写的一个函数对象的类型,CompareSet类重载函数调用运算符。

struct CompareSet
{
    bool operator()(const Point &lhs, const Point &rhs) const
    {
        cout << "bool operator()(const Point &, const Point &)" << endl;
        //...一些比较操作,省去
    }
};
void test()
{
    /* set<Point> number = { */
    /* set<Point, std::greater<Point>> number = { */
    set<Point, CompareSet> number = {
        Point(1, 2),
        Point(2, 3),
        Point(-2, 3),
        Point(2, -4),
        Point(12, 3),
    };
    display(number);
}

删除:

erase()

void erase( iterator pos );
iterator erase( iterator pos );
iterator erase( const_iterator pos );

void erase( iterator first, iterator last );
iterator erase( const_iterator first, const_iterator last );

size_type erase( const Key& key );
template< class K >
size_type erase( K&& x );
2.multiset

一切与set大致相同,不同的是可以插入多个相同的元素,insert操作的返回值也不是pair对象,而是一个迭代器因为插入总是可以成功。

3.map

与set大致相同,不过,里面存储的对象的数据类型是pair类型,可以用make_pair(kay,value)函数模板创建pair对象。

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T>>
> class map;
//std::make_pair
template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );

template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );

template< class T1, class T2 >
constexpr std::pair<V1,V2> make_pair( T1&& t, T2&& u )
void test(){
    map<string, string> number = {
        {"021", "北京"},
        {"027", "武汉"},
        {"0755", "深圳"},
        {"022", "天津"},
        pair<string, string>("0653", "北京"),
        pair<string, string>("021", "南京"),
        std::make_pair("0712", "广州"),
        std::make_pair("022", "广州"),
    };
}

map的下标访问不仅是可以插入,也可以修改。

//map的使用:
number.operator[]("022").operator=("河南");
//下方一行代码调用point的构造函数构造临时对象,然后调用赋值运算符函数赋值。
number["10"] = Point(100, 200);
display(number);
4.multimap

insert()的返回值不是pair对象,而是迭代器,同multiset,因为插入总是会成功的。

不支持下标访问,因为可以存放key值一样的pair对象,故若支持,会发生二义性。

3. 无序关联式容器
1.unordered_set
template<
    class Key,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator<Key>
> class unordered_set;
//自定义类型时,要进行的一些操作
//标准命名空间的扩展
namespace  std
{//模板的特化
template <>
struct hash<Point>
{
    size_t operator()(const Point &rhs) const
    {
        return ((rhs.getX() << 1 ) ^ (rhs.getY() << 2));
    };
};
}//end of namespace std

//或者
#if 1
struct hashPoint
{
    size_t operator()(const Point &rhs) const
    {
        cout << "size_t operator()(const Point &)" << endl;
        return ((rhs.getX() << 1 ) ^ (rhs.getY() << 2));
    };
};
#endif

bool operator==(const Point &lhs, const Point &rhs)
{
    return (lhs.getX() == rhs.getX()) && (lhs.getY() == rhs.getY());
}

void test()
{
    /* unordered_set<Point> number = { */
    unordered_set<Point, hashPoint> number = {
        Point(1, 2),
        Point(1, -2),
        Point(-1, 3),
        Point(1, 2),
        Point(7, 8),
    };
    display(number);
    cout << endl;
    number.insert(Point(1, 3));
    display(number);
}

特点:

  1. key值是唯一的,不能重复
  2. key值是没有顺序的
2.unordered_map
template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
3.unordered_multiset

同multiset,需要注意的是hash和KeyEqual。

4.unordered_multimap

同multimap,需要注意的是hash和KeyEqual。

4.容器适配器[没有迭代器,不能用迭代器进行遍历]
  1. Stack

    template<
        class T,
        class Container = std::deque<T>
    > class stack;
    

    成员函数:

    top
    empty
    size    
    push    
    emplace    
    pop
    void swap( stack& other )
  2. queue队列

    template<
        class T,
        class Container = std::deque<T>
    > class queue;
    

    成员函数:

    front
    back
    Capacity
    empty
    size
    push
    emplace
    pop
    swap
    
  3. 优先级队列priority_queue,可以指定底层实现的container和比较方式compare。

    template<
        class T,
        class Container = std::vector<T>,
        class Compare = std::less<typename Container::value_type>
    > class priority_queue;
    

    底层实现使用的是堆排序,默认情况是一个大根堆。当有元素插入进来的时候,进行堆的插入,先将插入的元素插入到堆尾,然后大元素不断上升。pop()的时候删除堆顶元素,将堆尾元素移动到堆顶,然后小元素不断下坠的过程。

    成员函数:

    top
    empty
    size
    push
    emplace
    pop
    swap
    

二.迭代器

  相比容器来说,是更高一层的抽象,对容器中的元素进行访问,不需要知道该元素位于哪个容器中,将容器做了隐藏,屏蔽了底层容器的实现。

  不同的算法对迭代器的要求不同,为此,STL定义了5种迭代器,分别是:

  • 随机访问迭代器

  • 双向迭代器

  • 前向迭代器

  • 输出迭代器

  • 输入迭代器

  • 请添加图片描述

    相关的操作:

    迭代器类别输出输入前向双向随机访问
    =*p=*p=*p=*p
    访问->->->-> [ ]
    *p=*p=*p=*p=
    迭代++++++++ –++ – + - += -=
    比较== !=== !=== !=== != < > <= >=

  将流迭代器当做容器来看待,在头文件中。

1.输出流迭代器

template< class T,
          class CharT = char,
          class Traits = std::char_traits<CharT> >
class ostream_iterator : public std::iterator<std::output_iterator_tag,
                                              void, void, void, void>

template< class T,
          class CharT = char,
          class Traits = std::char_traits<CharT>>
class ostream_iterator;
//构造:
ostream_iterator( ostream_type& stream, const CharT* delim );

ostream_iterator( ostream_type& stream );
vector<int>  number = {1, 3, 6, 8, 9, 2};
ostream_iterator<int> osi(cout, "\n");
copy(number.begin(), number.end(), osi);

配合copy()的源码,看看这三行代码背后会发生什么事(运行时会每行输出一个数字)。主要是ostream_iterator的几个运算符的重载:

template< class InputIt, class OutputIt >
OutputIt copy( InputIt first, InputIt last, OutputIt d_first );

template< class InputIt, class OutputIt >
constexpr OutputIt copy( InputIt first, InputIt last, OutputIt d_first );
//ostream_iterator类的数据成员
ostream_type* _M_stream;
const _CharT* _M_string;

//ostream_iterator类的构造函数
ostream_iterator(ostream_type& __s) : _M_stream(&__s), _M_string(0) {}
ostream_iterator(ostream_type& __s, const _CharT* __c) 
: _M_stream(&__s), _M_string(__c)  {}

//osi(cout, "\n")内部做的:
//_M_stream = &cout;
//_M_string = "\n";

//copy()函数的源码调用过程
template <class _InputIter, class _OutputIter>
inline _OutputIter copy(_InputIter __first, _InputIter __last,_OutputIter __result) {
  __STL_REQUIRES(_InputIter, _InputIterator);
  __STL_REQUIRES(_OutputIter, _OutputIterator);
  return __copy_aux(__first, __last, __result, __VALUE_TYPE(__first));
}

template <class _InputIter, class _OutputIter, class _Tp>
inline _OutputIter __copy_aux(_InputIter __first, _InputIter __last,
                              _OutputIter __result, _Tp*) {
  typedef typename __type_traits<_Tp>::has_trivial_assignment_operator _Trivial;
  return __copy_aux2(__first, __last, __result, _Trivial());
}
    
template <class _InputIter, class _OutputIter>
inline _OutputIter __copy_aux2(_InputIter __first, _InputIter __last,
                               _OutputIter __result, __false_type) {
  return __copy(__first, __last, __result,
                __ITERATOR_CATEGORY(__first),
                __DISTANCE_TYPE(__first));
}

template <class _InputIter, class _OutputIter>
inline _OutputIter __copy_aux2(_InputIter __first, _InputIter __last,
                               _OutputIter __result, __true_type) {
  return __copy(__first, __last, __result,
                __ITERATOR_CATEGORY(__first),
                __DISTANCE_TYPE(__first));
}

template <class _InputIter, class _OutputIter, class _Distance>
inline _OutputIter __copy(_InputIter __first, _InputIter __last,_OutputIter __result,
                          input_iterator_tag, _Distance*)
{
  for ( ; __first != __last; ++__result, ++__first)
    *__result = *__first;
  return __result;
}

//================================================
f                    l
1  3   6   8   9   2
       
*__result = 1
*osi = 1
osi = 1  
//ostream_iterator重载的一些运算符
ostream_iterator<_Tp>& operator*() { return *this; }
ostream_iterator<_Tp>& operator++() { return *this; } 
ostream_iterator<_Tp>& operator++(int) { return *this; } 

ostream_iterator<_Tp>& operator=(const _Tp& __value)
{ 
    *_M_stream << __value;//cout << 3
    if (_M_string) *_M_stream << _M_string;//cout << "\n" 
    return *this;
}

2.输入流迭代器

template< class T,
          class CharT = char,
          class Traits = std::char_traits<CharT>,
          class Distance = std::ptrdiff_t >
class istream_iterator: public std::iterator<std::input_iterator_tag,
                                             T, Distance, const T*, const T&>

template< class T,
          class CharT = char,
          class Traits = std::char_traits<CharT>,
          class Distance = std::ptrdiff_t >
class istream_iterator;
//构造:
istream_iterator();
constexpr istream_iterator();
constexpr istream_iterator( std::default_sentinel_t );

istream_iterator( istream_type& stream );
istream_iterator( const istream_iterator& other );
istream_iterator( const istream_iterator& other ) = default;
vector<int>  number;
istream_iterator<int> isi(std::cin);
//对于vector插入元素需要push_back
//copy(isi, istream_iterator<int>(),number.begin());//error 
copy(isi, istream_iterator<int>(), back_inserter(number));
copy(number.begin(), number.end(), ostream_iterator<int>(cout, "\n"));
//istream_iterator的数据成员
istream_type* _M_stream;
_Tp _M_value;
bool _M_ok;
//构造函数
istream_iterator(istream_type& __s) 
: _M_stream(&__s)                  //_M_stream = cin
{ 
    _M_read(); 
}
void _M_read() 
{
    _M_ok = (_M_stream && *_M_stream) ? true : false;
    if (_M_ok) {
		*_M_stream >> _M_value;     //cin >> _M_value,_M_value = 1(输入的1)
		_M_ok = *_M_stream ? true : false;  //_M_ok = 1
    }
}
//copy()算法核心
//赋值后各值:
//__first = isi
//__last = istream_iterator<int>()
//result = back_inserter(number)
template <class _InputIter, class _OutputIter, class _Distance>
inline _OutputIter __copy(_InputIter __first, _InputIter __last,_OutputIter __result,
                          input_iterator_tag, _Distance*)
{
  //在++first时进行读入,在*__result=*__first时进行push_back
  for ( ; __first != __last; ++__result, ++__first)
    *__result = *__first;
  return __result;
}
//istream_iterator重载的一些运算符
reference operator*() const { return _M_value; }
istream_iterator& operator++() { 
    _M_read(); 
    return *this;
}
//back_inserter()背后做的事情
template <class _Container>
inline back_insert_iterator<_Container> back_inserter(_Container& __x) {
  	return back_insert_iterator<_Container>(__x);
}
//back_insert_iterator类的构造函数
explicit back_insert_iterator(_Container& __x) : container(&__x) {}
//重载的一些运算符
back_insert_iterator<_Container>& operator*() { return *this; }
back_insert_iterator<_Container>& operator++() { return *this; }
back_insert_iterator<_Container>& operator++(int) { return *this; }

back_insert_iterator<_Container>&
operator=(const typename _Container::value_type& __value) { 
    container->push_back(__value);
    return *this;
}

3.一些相关的迭代器适配器

back_inserter()   back_insert_iterator类

front_inserter()   front_insert_iterator类

inserter()         inserter_iterator类

前一列调用返回了后一列的对象,后一列重载了赋值运算符函数来进行不同的插入操作。

4.反向迭代器和插入迭代器

reverse_iterator 和 .rbegin()、.rend()连用

insert_iterator的使用:

vector<int> vecNumber = {1, 4, 9, 3, 6};
set<int> setNumber = {100, 200, 300};
auto it = setNumber.begin();
copy(vecNumber.begin(), vecNumber.end(), insert_iterator<set<int>>(setNumber, it));
copy(setNumber.begin(), setNumber.end(), ostream_iterator<int>(cout, "  "));

三.适配器

  前面简要提到了适配器的概念,适配器相当于提供了一个接口,使得某些不适用于特定对象的方法可以被该对象使用;当容器或函数对象无法直接应用于算法,必须有一种中间过渡机制来实现两者的匹配,这就是适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。

  1. 容器适配器:stack、queue、priority_queue。

  2. 迭代器适配器:流迭代器、反向迭代器、插入迭代器

  3. 函数适配器:bind1st、bind2nd、bind

    bind1st、bind2nd函数绑定器,绑定一个二元函数和它某个参数:

    template< class F, class T >
    std::binder1st<F> bind1st( const F& f, const T& x );
    
    template< class F, class T >
    std::binder2nd<F> bind2nd( const F& f, const T& x );
    
    //保留number里大于5的值的几种做法
    auto it = remove_if(number.begin(), number.end(), 
                            std::bind2nd(std::greater<int>(), 5));
    
    /* auto it = remove_if(number.begin(), number.end(), */ 
    /*                     std::bind1st(std::less<int>(), 5)); */
    
    /* std::less<int> les; */
    /* auto it = remove_if(number.begin(), number.end(),std::bind1st(les, 5)); */
    

    bind()的使用:有点类似函数对象跟函数指针。

    template< class F, class... Args >
    bind( F&& f, Args&&... args );
    
    template< class R, class F, class... Args >
    bind( F&& f, Args&&... args );
    

    bind可以绑定任意个参数的函数,也可以绑定类的成员函数,但是要写成&类名::函数名的形式,甚至可以绑定数据成员。对于不事先绑定的参数,需要传std::placeholders,从_1开始,依次递增。

    占位符std::placeholders::_x的用法:

    1. 占位符本身表示的是形参的位置
    2. 占位符中的数字代表的是实参传过来的位置
  4. bind参数传递使用的是值传递,要想使用引用必须用引用的包装器。std::ref( )和std::cref( ),const reference 引用的包装器。

  5. bind可以改变函数的类型和函数的形态。

  6. std::function<R(Args…)>:函数的容器

    template< class >
    class function; /* undefined */
    
    template< class R, class... Args >
    class function<R(Args...)>;
    
    void test()
    {
        //int (int, int) ===> int (),
        //函数类型 int ()
        /* auto f = bind(add, 1, 2); */
        //函数的容器
        std::function<int()> f = bind(add, 1, 2);
        cout << "f() = " << f() << endl;
    
        cout << endl;
        /* auto f1 = bind(multiply, 2, 3, 4); */
        std::function<int()> f1 = bind(multiply, 2, 3, 4);
        cout << "f1() = " << f1() << endl;
    
        cout << endl;
        Example ex;
        //bind绑定成员函数
        /* auto f2 = bind(&Example::add, &ex, 3, 4); */
        f1 = bind(&Example::add, &ex, 3, 4);
        cout << "f1() = " << f1() << endl;
    
        cout << endl;
        //bind绑定数据成员
        f1 = bind(&Example::data, &ex);
        cout << "data : f1() = " << f1() << endl;
    
        //int (int, int)===>int add(1, xxxx)
        cout << endl;
        /* auto f3 = bind(add, 1, std::placeholders::_1); */
        std::function<int(int)> f3 = bind(add, 1, std::placeholders::_1);
        cout << "f3(100) = " << f3(100) << endl;
    
        cout << endl;
        //占位符
        /* auto f4 = bind(add, std::placeholders::_3, std::placeholders::_1); */
        std::function<int(int, int, int)> f4 = bind(add, std::placeholders::_3, std::placeholders::_1);
        cout << "f4(100, 200, 300) = " << f4(100, 200, 300) << endl;
    }
    
    void test3()
    {
        //占位符本身表示的是形参的位置
        //占位符中的数字代表的是实际参数传过来的位置
        //参数传递使用的是值传递
        //cref,const reference 引用的包装器
        int number = 300;
        auto f = bind(func4, 10, std::placeholders::_8, std::placeholders::_3,
             std::cref(number), number);
        number = 500;
    
        f(1, 3, 5, 7, 9, 10, 30, 40);//1, 3, 7这样的数据起到占位置,本身没有什么作用
    
    }
    
  7. function与bind实现多态:

    class Figure
    {
    public:
        /* typedef std::function<void()> DisplayCallback; */
        using DisplayCallback = std::function<void()>;
        using AreaCallback = std::function<double()>;
        
        DisplayCallback _displayCallback;
        AreaCallback _areaCallback;
    
        //注册回调函数
        void setDisplayCallck(DisplayCallback &&cb)
        {
            _displayCallback = std::move(cb);
        }
    
        void setAreaCallback(AreaCallback &&cb)
        {
            _areaCallback = std::move(cb);
        }
        //执行回调函数
        void handDisplayCallback() const
        {
            if(_displayCallback)
            {
               _displayCallback();
            }
        }
        double handAreaCallback() const
        {
            if(_areaCallback)
            {
                return _areaCallback();
            }else{
                return 0;
            }
        }
    };
    
    class Rectangle
    {
    public:
        Rectangle(double len = 0, double width = 0)
        : _length(len)
        , _width(width)
        {
            cout << "Rectangle(double = 0, double = 0) " << endl;
        }
        void display(int y) const 
        {
            cout << "Rectangle : " ;
        }
        double area() const 
        {
            return _length * _width;
        }
        ~Rectangle()
        {
            cout << "~Rectangle()" << endl;
        }
    private:
        double _length;
        double _width;
    };
    
    void func(const Figure &ref)
    {
        ref.handDisplayCallback();
        cout << ref.handAreaCallback() << endl;
    }
    int main(int argc, char **argv)
    {
        Rectangle rectagle(10, 20);
        Figure figure;
        //void (int)===>void ()
        figure.setDisplayCallck(std::bind(&Rectangle::display, &rectagle, 10));
        figure.setAreaCallback(std::bind(&Rectangle::area, &rectagle));
        func(figure);
        return 0;
    }
    
  8. 成员函数适配器mem_fn()

    class Number
    {
    public:
        Number(size_t data = 0)
        : _data(data){}
    
        void print() const{cout << _data << " ";}
    
        bool isEven() const{return (0 == _data % 2);}
        bool isPrime() const{
            if(1 == _data){return false;}
            //质数/素数
            for(size_t idx = 2; idx <= _data/2; ++idx){
                if(0 == _data % idx){return false;}
            }
            return true;
        }
    private:
        size_t _data;
    }
    void test()
    {
        vector<Number> numbers;
        for(size_t idx = 1; idx != 30; ++idx)
        {
            numbers.push_back(Number(idx));
        }
    
        std::for_each(numbers.begin(), numbers.end(), mem_fn(&Number::print));
        cout << endl;
    
        //erase函数参数之前说过,两个参数
        numbers.erase(remove_if(numbers.begin(), numbers.end(), mem_fn(&Number::isEven)), numbers.end());
        std::for_each(numbers.begin(), numbers.end(), mem_fn(&Number::print));
        cout << endl;
    
        numbers.erase(remove_if(numbers.begin(), numbers.end(), 
                                mem_fn(&Number::isPrime)), numbers.end());
        std::for_each(numbers.begin(), numbers.end(), mem_fn(&Number::print));
        cout << endl;
    }
    

四.算法

位于头文件algorithm,操作数据。

一元函数(UnaryFunction):函数的参数只有一个

一眼断言/一元谓词(UnaryPredicate):函数的参数是一个,函数的返回类型是bool

C++11中的lambda表达式,匿名函数,用“[ ]”来表示是匿名函数。

如:

for_each(number.begin(), number.end(), [] (int &value) {++value;cout << value << "  ";});

1.非修改式算法

for_each()

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

template< class ExecutionPolicy, class ForwardIt, class UnaryFunction2 >
void for_each( ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, UnaryFunction2 f );
void print(int &value)
{
    ++value;
    cout << value << "  ";
}
void test()
{
    vector<int> number = {1, 3, 6, 8, 9, 3, 5, 6};
    /* for_each(number.begin(), number.end(), print); */
    //C++11中的lambda表达式,匿名函数
    for_each(number.begin(), number.end(), [] (int &value) {
             ++value;
             cout << value << "  ";
             });
    cout << endl;
    copy(number.begin(), number.end(), ostream_iterator<int>(cout,  "  "));
    cout << endl;
}

count()

template< class InputIt, class T >
typename iterator_traits<InputIt>::difference_type
    count( InputIt first, InputIt last, const T& value );

find()

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

binary_search()

template< class ForwardIt, class T >
bool binary_search( ForwardIt first, ForwardIt last, const T& value );

template< class ForwardIt, class T, class Compare >
bool binary_search( ForwardIt first, ForwardIt last, const T& value, Compare comp );

sort()

template< class RandomIt >
void sort( RandomIt first, RandomIt last );

2.修改式算法

remove_if()

template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );
void test()
{
    vector<int> number = {1, 3, 5, 7, 4, 3, 2, 6, 8, 9, 1};
    copy(number.begin(), number.end(), ostream_iterator<int>(cout,  "  "));
    cout << endl;

    auto it = remove_if(number.begin(), number.end(), 
                        std::bind2nd(std::greater<int>(), 5));

    /* auto it = remove_if(number.begin(), number.end(), */ 
    /*                     std::bind1st(std::less<int>(), 5)); */

    /* std::less<int> les; */
    /* auto it = remove_if(number.begin(), number.end(), */ 
                        /* std::bind1st(les, 5)); */

    /* auto it = remove_if(number.begin(), number.end(), [] (int value) */
    /*                     { */
    /*                     return value > 5; */
    /*                     }); */
    /* auto it = remove_if(number.begin(), number.end(), display); */
    number.erase(it, number.end());
    copy(number.begin(), number.end(), ostream_iterator<int>(cout,  "  "));
    cout << endl;
}

查看remove_if()源码,看内部到底做了什么,为什么没有预期的删除的结果?

remove_if()的返回值是待删除元素的第一个首迭代器,该操作后,用erase()操作删除后即是预期的结果。

//first = number.begin()
//last = number.end()
//p = display
bool display(int value)
{
    return value > 5;
}

template<class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
    first = std::find_if(first, last, p);
    if (first != last)
        for(ForwardIt i = first; ++i != last; )
            if (!p(*i))
                *first++ = std::move(*i);
    return first;
}

first
1, 3, 5, 7, 4, 3, 2, 6, 8, 9, 1
    							last
    
template<class InputIt, class UnaryPredicate>
constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p)
{
    for (; first != last; ++first) {
        if (p(*first)) {
            return first;
        }
    }
    return last;
}

在读容器vector的过程中不要去写,因为可能会发生扩容,然后迭代器失效,导致读的迭代器不能正常访问。


五.函数对象(仿函数)

将所有的函数形式进行统筹,重载了函数调用运算符的类创建的对象,普通函数,函数指针,成员函数。

函数对象是可以以函数方式与()结合使用的任意对象,包括:函数名,指向函数的指针,重载了()操作符的类对象。


六.空间配置器allocator

管理空间的申请和释放,在使用时感受不到它的存在,进行内存管理。

//std::allocator
//Defined in header <memory>
template< class T >
struct allocator;

template<>
struct allocator<void>;
1.主要接口

申请空间:

pointer allocate( size_type n, const void * hint = 0 );
T* allocate( std::size_t n, const void * hint);

T* allocate( std::size_t n );
constexpr T* allocate( std::size_t n );

释放空间:

void deallocate( T* p, std::size_t n );
constexpr void deallocate( T* p, std::size_t n );

创建对象:

void construct( pointer p, const_reference val );

template< class U, class... Args >
void construct( U* p, Args&&... args );

销毁对象:

void destroy( pointer p );

template< class U >
void destroy( U* p );

应用举例:

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

template <class T>
class Vector
{
public:
   /* typedef T * iterator; //C语言中的重定义*/
   using iterator = T *;//C++11的重定义方式

   Vector()
   : _start(nullptr)
   , _finish(nullptr)
   , _end_of_storage(nullptr)
   {}
   
   ~Vector();
   void push_back(const T &t);
   void pop_back();

   iterator begin()
   {
       return _start;
   }

   iterator end()
   {
       return _finish;
   }

   int size() const
   {
       return _finish - _start;
   }

   int capacity() const
   {
       return _end_of_storage - _start;
   }
private:
   void reallocator();
private:
   static std::allocator<T> _alloc;//静态数据成员,放在类外初始化
   T *_start;
   T *_finish;
   T *_end_of_storage;
};

template <class T>  
std::allocator<T> Vector<T>::_alloc;

template<class T>
Vector<T>::~Vector()
{
   if(_start)
   {
       while(_finish != _start)
       {
           _alloc.destroy(--_finish);//对象的销毁
       }
       _alloc.deallocate(_start, capacity());//空间的释放
   }
}

template <class T>
void Vector<T>::push_back(const T &t)
{
   if(size() == capacity())
   {
       reallocator();//当size() == capacity(),需要扩容
   }
   if(size() < capacity())
   {
       _alloc.construct(_finish++, t);
   }
}

template <class T>
void Vector<T>::pop_back()
{
   if(size() > 0)
   {
       _alloc.destroy(--_finish);
   }

}

template <class T>
void Vector<T>::reallocator()
{
   int oldCapacity = capacity();
   int newCapacity = 2 * oldCapacity > 0 ? 2 * oldCapacity : 1;

   T *ptmp = _alloc.allocate(newCapacity);//开辟空间,该空间上是没有对象
   if(_start)
   {
       /* copy(_start, _finish, ptmp); //copy算法中需要对对象进行解引用,对象进行赋值,但对象本身并不存在,未初始化*/
       std::uninitialized_copy(_start, _finish, ptmp);
       while(_finish != _start)
       {
           _alloc.destroy(--_finish);//对象的销毁
       }
       _alloc.deallocate(_start, capacity());//空间的释放
   }
   //三个指针,需要重新设置
   _start = ptmp;
   _finish = _start + oldCapacity;
   _end_of_storage = _start + newCapacity;
}

template <class Container>
void display(const Container &vec)
{
   cout << "vec.size() = " <<vec.size() << endl
        << "vec.capacity() = " << vec.capacity() << endl;

}

void test()
{
   Vector<int> numbers;
   display(numbers);

   cout << endl;
   numbers.push_back(1);
   display(numbers);

   cout << endl;
   numbers.push_back(2);
   display(numbers);

   cout << endl;
   numbers.push_back(3);
   display(numbers);

   cout << endl;
   numbers.push_back(4);
   display(numbers);

   cout << endl;
   numbers.push_back(5);
   display(numbers);

   cout << endl;
   for(auto &elem : numbers)
   {
       cout << elem << " ";

   }
   cout<< endl;;

}
int main(int argc, char **argv)
{
   test();
   return 0;
}

2.源码剖析
Tp* allocate(size_type __n, const void* = 0) 
{
    return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp))) : 0;
}

void deallocate(pointer __p, size_type __n)
{ 
    _Alloc::deallocate(__p, __n * sizeof(_Tp)); 
}

void construct(pointer __p, const _Tp& __val) 
{ 
    new(__p) _Tp(__val); //定位new表达式,指定的空间上构建对象
}

void destroy(pointer __p) 
{ 
    __p->~_Tp(); 
}
typedef alloc _Alloc;

//分支1,一级空间配置器
#ifdef __USE_MALLOC   
typedef malloc_alloc alloc;

typedef __malloc_alloc_template<0> malloc_alloc;

template <int __inst>
class __malloc_alloc_template 
{
publicstatic void* allocate(size_t __n)
  	{
   		void* __result = malloc(__n);
    	if (0 == __result) __result = _S_oom_malloc(__n);
    	return __result;
  	}
}
template <int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
    void (* __my_malloc_handler)();
    void* __result;
    for (;;) {
        __my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*__my_malloc_handler)();
        __result = malloc(__n);
        if (__result) return(__result);
    }
}


//分支2,二级空间配置器
#else                 
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;

class __default_alloc_template
{
   enum {_ALIGN = 8};
   enum {_MAX_BYTES = 128};
   enum {_NFREELISTS = 16};
    
   union _Obj {
        union _Obj* _M_free_list_link;
        char _M_client_data[1];    /* The client sees this.        */
   }; 
    
   static _Obj* __STL_VOLATILE _S_free_list[]; 
public:
     static void* allocate(size_t __n)
  	 {
    	void* __ret = 0;
    	if (__n > (size_t) _MAX_BYTES) {   //if n > 128
      		__ret = malloc_alloc::allocate(__n);
    	}
    	else {
            //内存池+16个自由链表
      		_Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n);
      		_Obj* __RESTRICT __result = *__my_free_list;
      		if (__result == 0)
        		__ret = _S_refill(_S_round_up(__n));
      		else {
        		*__my_free_list = __result -> _M_free_list_link;
        		__ret = __result;
      		}
    	}
    	return __ret;
  	};
    
    //找到存放对应大小自由链表位置的数组下标
    static  size_t _S_freelist_index(size_t __bytes) 
    {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  	}
    //向上取整,找到8的倍数
    static size_t _S_round_up(size_t __bytes) 
    { 
        return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); 
    }
}

template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;

template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;

template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;


template <bool __threads, int __inst>
void* __default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{																																					//__n = 32为例
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs); //32 20,申请了640个字节交给了__chunk
    											//同时有640个字节交给了内存池
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[3]
    
    /* Build free list in chunk */
    __result = (_Obj*)__chunk;
    *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
    for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
    }
    return(__result);
}


template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, 
                                                            int& __nobjs)
{//32  20
    char* __result;
    size_t __total_bytes = __size * __nobjs;             //32*20 = 640
    size_t __bytes_left = _S_end_free - _S_start_free;   //初始 0 = 0 - 0

    if (__bytes_left >= __total_bytes) {
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } else if (__bytes_left >= __size) {
        __nobjs = (int)(__bytes_left/__size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } else {
        size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
        // __bytes_to_get = 2*640+0 = 1280
        // Try to make use of the left-over piece
        //把内存池中的空间分配给自由链表中应该分配的位置,因为小于__size,所以刚好是对应的一块空间
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        //malloc(1280)
        _S_start_free = (char*)malloc(__bytes_to_get);
        if (0 == _S_start_free) 
        {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
	    	_Obj* __p;
            for (__i = __size; __i <= (size_t)_MAX_BYTES; __i += (size_t) _ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));
                }
            }
	    	_S_end_free = 0;	// In case of exception.
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
        }
        _S_heap_size += __bytes_to_get;  //_S_heap_size = 1280
        _S_end_free = _S_start_free + __bytes_to_get; //两个指针分别指向头和尾
        return(_S_chunk_alloc(__size, __nobjs)); //递归调用
    }
}

//16个自由链表
template <bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _NFREELISTS
# else
    __default_alloc_template<__threads, __inst>::_NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

allocate的操作:

  1. 当申请的空间大于128字节时,直接使用malloc:一级空间配置器

  2. 当申请的空间小于等于128字节时,使用内存池+16个自由链表:二级空间配置器

请添加图片描述

  1. 具体细节

    1. 当内存池空间大于要申请的大小为n的空间,会把对应数量的空间chunk后挂在对应的自由链表下。
    2. 当内存池为空或者小于要申请大小为n的空间,会一次申请20*n的大小,然后10个chunk后挂在对应的自由链表上,剩下的10个大小的放在内存池,用_S_start_free和 _S_end_free来管理。
    3. 当内存池空间为空或者小于要申请的大小为n的空间,且堆上没有空间可申请,会挨个遍历比N大的自由链表上,是不是有空间,要是有,则把一个链表结点代表的空间归还到内存池,然后分配。若没有,转为一级空间配置器。

deallocate操作:
  会将申请的空间归还到自由链表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值