STL 概览

概述

网上常见的说法是 STL 包含六大组件。

  • 容器 container
  • 算法 algorthm
  • 迭代器 iterator
  • 仿函数 function object
  • 适配器 adaptor
  • 空间配置器 allocator

抄袭链接:https://www.jianshu.com/p/497843e403b4

容器

下面列举几种常见的容器:

  • vector 容器
  • deque 双端数组
  • stack 栈模型
  • queue 队列模型
  • list 链表模型
  • priotriy_queue 优先级队列
  • set 与 multiset 容器
  • map 与 multimap 容器
  • string

算法

STL 中的算法可以分为以下几类。(网上抄的,链接找不到了)

  • 只读算法:查找和计数
  • 可变序列算法:复制、变换、替换、填充、移除和随机生成
  • 排序算法
  • 比较算法
  • 堆算法
  • 各个容器特有算法

迭代器

迭代器提供了用于遍历元素的 “指针”,容器和算法之间可以通过迭代器联系起来。迭代器有“前向”和“后向”的区别,有“常量”和“非常量”的区别。对于迭代器,++i 通常是要比 i++ 要快的。比如 vector 中实现为这个样子:

_Vector_iterator& operator++() noexcept {
    _Mybase::operator++();
    return *this;
}

_Vector_iterator operator++(int) noexcept {
    _Vector_iterator _Tmp = *this;
    _Mybase::operator++();
    return _Tmp;
}

仿函数

在没有 lambda std::function, 之前通过重载 operator() 来实现类似函数的行为。比如下面的 plus,实际上是一个结构体,重载了圆括号操作符,它的对象就可以像函数一样被调用了。

// 库函数中 plus 的实现
// STRUCT TEMPLATE plus
template <class _Ty = void>
struct plus {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _RESULT_TYPE_NAME;

    _NODISCARD constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left + _Right;
    }
};

// 自己实现一个
template <typename T, typename Container>
struct Sum
{
	Sum() {
		cout << "Sum construct" << endl;
	}
	T operator()(Container container) {
		T acc = T(0);
		for (auto x : container) {
			acc += x;
		}
		return acc;
	}
};

int main() {
	vector<int> arr = { 1, 2, 3, 5 };
	auto x = accumulate(arr.cbegin(), arr.cend(), 0, plus<int>());
	Sum<decltype(arr)::value_type, decltype(arr)> sum_functor;
	Sum<decltype(arr)::value_type, decltype(arr)> sum_functor1{};
	Sum<decltype(arr)::value_type, decltype(arr)> sum_functor2 = Sum<decltype(arr)::value_type, decltype(arr)>();
	Sum<decltype(arr)::value_type, decltype(arr)> sum_functor3(); // 这个是啥语法啊
	auto y = sum_functor(arr);
	cout << x << " " << y << endl;
	return 0;
}

适配器

一种设计模型。这就好像香港的充电器需要使用一个适配器才可以插到插座上。

STL 当中的 stack, queue, priority_queue 三种容器就是适配器。对于适配器,它可以接收一个容器,然后提供 stack 需要的接口,比如 push, pop 等方法。内部实现就是调用容器的方法去 push, pop。

template <class _Ty, class _Container = deque<_Ty>>
class stack;

template <class _Ty, class _Container>
class stack {
public:
    using value_type      = typename _Container::value_type;
    using reference       = typename _Container::reference;
    using const_reference = typename _Container::const_reference;
    using size_type       = typename _Container::size_type;
    using container_type  = _Container;

    static_assert(is_same_v<_Ty, value_type>, "container adaptors require consistent types");

    stack() = default;

    explicit stack(const _Container& _Cont) : c(_Cont) {}
    
    // ...
}

分配器

负责内存管理,分配内存。allocator 提供了几个方法进行分配回收内存和对象构造析构:allocate, deallocate, construct, destory, address, max_size。如果调用 deallocate 没有调用 destory,那么不会调用类的析构函数。

参考:https://vimsky.com/zh-tw/examples/usage/stdallocator-in-cpp-with-examples.html

class Apple {
public:
	Apple() {
		cout << "apple construct" << endl;
	}
	~Apple() {
		cout << "apple desstruct" << endl;
	}
};

int main() {
	allocator<Apple> apple_alloc;
	Apple* apples = apple_alloc.allocate(10);
	apple_alloc.construct(apples);
	apple_alloc.construct(apples + 1);
	apple_alloc.destroy(apples);
	apple_alloc.destroy(apples + 1);
	return 0;
}

vector 中的 reverse 方法就是调用了 allocator 去分配内存。

void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) {
    // increase capacity to _Newcapacity (without geometric growth), provide strong guarantee
    if (_Newcapacity > capacity()) { // something to do (reserve() never shrinks)
        if (_Newcapacity > max_size()) {
            _Xlength();
        }

        _Reallocate_exactly(_Newcapacity);
    }
}

void _Reallocate_exactly(const size_type _Newcapacity) {
    // set capacity to _Newcapacity (without geometric growth), provide strong guarantee
    auto& _My_data    = _Mypair._Myval2;
    pointer& _Myfirst = _My_data._Myfirst;
    pointer& _Mylast  = _My_data._Mylast;

    const auto _Size = static_cast<size_type>(_Mylast - _Myfirst);

    const pointer _Newvec = _Getal().allocate(_Newcapacity);

    _TRY_BEGIN
    _Umove_if_noexcept(_Myfirst, _Mylast, _Newvec);
    _CATCH_ALL
    _Getal().deallocate(_Newvec, _Newcapacity);
    _RERAISE;
    _CATCH_END

    _Change_array(_Newvec, _Size, _Newcapacity);
}
posted @ 2021-11-13 19:58  楷哥  阅读(361)  评论(0编辑  收藏  举报