目录:
零碎知识:
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};
四种遍历方式:
-
for循环+下标
-
for+迭代器:vector<int>::const_iterator cit = number.begin()
-
for+广义指针:auto it = number.begin()
-
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);
}
特点:
- key值是唯一的,不能重复
- 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.容器适配器[没有迭代器,不能用迭代器进行遍历]
-
Stack
template< class T, class Container = std::deque<T> > class stack;
成员函数:
top empty size push emplace pop void swap( stack& other );
-
queue队列
template< class T, class Container = std::deque<T> > class queue;
成员函数:
front back Capacity empty size push emplace pop swap
-
优先级队列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, " "));
三.适配器
前面简要提到了适配器的概念,适配器相当于提供了一个接口,使得某些不适用于特定对象的方法可以被该对象使用;当容器或函数对象无法直接应用于算法,必须有一种中间过渡机制来实现两者的匹配,这就是适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。
-
容器适配器:stack、queue、priority_queue。
-
迭代器适配器:流迭代器、反向迭代器、插入迭代器
-
函数适配器: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的用法:
- 占位符本身表示的是形参的位置
- 占位符中的数字代表的是实参传过来的位置
-
bind参数传递使用的是值传递,要想使用引用必须用引用的包装器。std::ref( )和std::cref( ),const reference 引用的包装器。
-
bind可以改变函数的类型和函数的形态。
-
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这样的数据起到占位置,本身没有什么作用 }
-
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; }
-
成员函数适配器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
{
public:
static 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的操作:
-
当申请的空间大于128字节时,直接使用malloc:一级空间配置器
-
当申请的空间小于等于128字节时,使用内存池+16个自由链表:二级空间配置器
-
具体细节
- 当内存池空间大于要申请的大小为n的空间,会把对应数量的空间chunk后挂在对应的自由链表下。
- 当内存池为空或者小于要申请大小为n的空间,会一次申请20*n的大小,然后10个chunk后挂在对应的自由链表上,剩下的10个大小的放在内存池,用_S_start_free和 _S_end_free来管理。
- 当内存池空间为空或者小于要申请的大小为n的空间,且堆上没有空间可申请,会挨个遍历比N大的自由链表上,是不是有空间,要是有,则把一个链表结点代表的空间归还到内存池,然后分配。若没有,转为一级空间配置器。
deallocate操作:
会将申请的空间归还到自由链表