STL使用细节详解

目录

1.STL标准模板库

2.容器

 2.1 string容器

 2.2 数组(vector array valarray)

 2.3 链表(list forward_list)

 2.4 双端队列deque

 2.5 map 

3.几种简单算法

3.1排序

3.1.1  从小到大

3.1.2 从大到小

3.1.3  随机排序

3.2 遍历(三种for循环)

3.2.1 常规for循环( 在复杂的逻辑中效率高)

3.2.2  foreach循环(简洁易用,不容易发生错误,适用于数组、集合遍历循环未知的情况下是不能进行增删操作的)

3.2.3 for_each循环

4.迭代器

5.适配器

6.分配器

7.仿函数


1.STL标准模板库

先上图,对上述六部分内容使用上注意点细节进行详解。

2.容器

 2.1 string容器

 string在我们C语言当中,char *表示指向字符数组地址的指针。使用string类完全不用考虑内存分配和释放,也不用担心约界崩溃。使用注意以下几点:

  •  可以通过下表和at函数访问,使用at访问不需当心数组越界
  •  str.c_str()函数使用问题

        可以查看以下源码:

pointer
      _M_data() const
      { return _M_dataplus._M_p; }

        既然是一个地址值,就一定是一个常量。所以在delete可以使得指针有所指。

        我在实现此函数的时候是这样定义的char* Mystring::c_str(),如果不写引用,该函数将返回一个副本,期间不会涉及到对内容的修改,所以原指针不会同步状态给副本,会导致释放失败。

  • 行为区别
sizelengthcapacityreserve(来自algorithm头文件)
概念返回的字符串个数返回字符串长度系统为字符串分配所在空间大小(容器存储数据个数)重设分配空间大小

二者没有区别,可以看一下源码

 length源码:

length() const _GLIBCXX_NOEXCEPT
{ return _M_rep()->_M_length; }

size源码:

size() const _GLIBCXX_NOEXCEPT
{ return _M_rep()->_M_length; }

分配空间原则(不同编译器可能不同,g++测试)

当数据<15,系统开辟内存空间大小为15        

当内存不够的时候,会按照原来系统的大小整数陪递增

15--30--60--120.....

注意:Visual Studio和VC分配方式可能不同      

reserve修改容量不能变小,只能变大。

比如原先容量15,reserve(16),

则容量变为30,

reserve(1)

则容量仍为30

 2.2 数组(vector array valarray)

内存的分配原理跟string是一样的,是连续的空间,如果空间不够用,会申请一个更大的连续的空间,同时迭代器失效

vector
头文件#include <vector>
using namespace std;
语法格式

vector<int> vec;  

<>可以放基本数据类型、结构体、指针、对象

构造函数vector();无参数的构造
vector(size_type _Count);n个元素
vector( size_type num, const TYPE &val );用num个val来初始化容器
vector( const vector &from );

拷贝构造

vector( input_iterator start, input_iterator end );迭代器初始化
定义vector的迭代器vector<int>::iterator ite;

begin()  指向头

end()    指向尾元素的下一个元素

 
空间属性
 
empty()对象是否有元素
size()元素个数
resize()

重新设置元素个数

缩小时大小改变  容量不变,

放大时大小改变  容量改变

reserve()修改容量,不能变小只能变大。想修改多少就修改多少,不像是string变量必须按照整数倍来增加
capacity()按照初始化空间大小的整数倍增加。(比如初始化空间大小为5,当空间占用满再插入新的数据,空间容量会加倍)

查寻

注:相对于list、map来讲查询速度快(因为可以通过下标访问)

输出全部元素

循环(迭代器、下表运算)、for_each

输出单个元素: 

at、[]下标运算、back(返回尾巴元素)

 使用at的好处:检测下表是否越界,并做相应的处理

增加

注:由于是数组,尾添加效率非常高(不考虑重新增加空间)

       中间添加的效率很低(因为插入该元素后,后边的元素都要以此重新排列)

void push_back( const TYPE &val );

尾添加
 iterator insert( iterator loc, const TYPE &val );在指定迭代器的位置加入一个数据
void insert( iterator loc, size_type num, const TYPE &val );在某个迭代器后加入num个值为value的元素
void insert( iterator loc, input_iterator start, input_iterator end );在某个迭代器后加入另一个向量的中间一段
删除void pop_back();尾删除
iterator erase( iterator loc );删除一个指定
iterator erase( iterator start, iterator end )删除一段指定
void clear();删除所有
修改利用输出的形式可以修改
赋值函数(重新赋值,清除以前的) 
运算符重载v1 == v2
v1 != v2
v1 <= v2
v1 >= v2
v1 < v2
v1 > v2 
所有相同位置的元素相等,比较的形式跟字符串一样
遍历(下面细说)

排序
乱序

 2.3 链表(list forward_list)

         list是双向链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。           

                        

头文件#include <list>
using namespace std;
语法格式

list<int> lis;  

<>可以放基本数据类型、结构体、指针、对象

双向循环链表
构造函数list()无参数的构造
list( size_type _Count);多个元素,初始值为0
list( size_type _Count,   const Type& _Val);多个指定的值
list( const _list& _Right);

拷贝构造(用一个list初始化当前的list)

list( InputIterator _First, InputIterator _Last   );用另一个对象中间的一段对该链表初始化
定义vector的迭代器list<int>::iterator ite;

begin()  指向头

end()    指向尾元素的下一个元素,不能进行加法运算,可以自加++

 
空间属性
 
empty()对象是否有元素
size()元素个数
resize()

重新设置元素个数

缩小时大小改变  容量不变,

放大时大小改变  容量改变

没有reserve(),因为是链表
没有capacity(),因为是链表

查寻

注:相对于数组来讲查询速度慢(因为需要遍历查询)

输出全部元素

循环(迭代器、下表运算)、for_each

输出单个元素: 

1.[]下标运算、2.back(返回尾巴元素)

3.front()(返回第一个元素)

增加

注:插入删除速度快,只需要修改前一个节点和后一个节点指向

void  push_front(const TYPE &val ) 头添加

void push_back( const TYPE &val );

尾添加
 iterator insert( iterator loc, const TYPE &val );在指定迭代器的位置加入一个数据
void insert( iterator loc, size_type num, const TYPE &val );在某个迭代器后加入num个值为value的元素
void insert( iterator loc, input_iterator start, input_iterator end );在某个迭代器后加入另一个向量的中间一段
删除void  pop_front()头删除
void pop_back();尾删除
iterator erase( iterator loc );删除一个指定
iterator erase( iterator start, iterator end )删除一段指定
void clear();删除所有
void remove( const TYPE &val );删除所有跟参数相同的元素。如果是结构体需要重载==
void unique(void) 删除list中重复的元素 
修改利用迭代器修改
赋值函数assign
赋值=修改
运算符重载v1 == v2
v1 != v2
v1 <= v2
v1 >= v2
v1 < v2
v1 > v2 
所有相同位置的元素相等,比较的形式跟字符串一样
  void swap( list &from );交换两个list的内容
void reverse();reverse() 把list的元素翻转
void sort();默认从小到大排序,如果容器本身自带排序,那么使用的时候就可以不用选择排序算法
void merge( list &lst );

合并两个list。

特点:

1.自动排序

2.重载小于号

3.两个链表必须有序(如果链表元素是升序的,就要用<号,return true,反之)

void splice( iterator pos, list &lst );
void splice( iterator pos, list &lst, iterator del );
void splice( iterator pos, list &lst, iterator start, iterator end );
拼接

forward_list是一个单向链表,只支持单向顺序访问,在链表的任何位置进行插入/删除操作都非常快(为了提高效率没有添加size函数)

一般通常使用中,对效率没有非常细微要求都可以。

,注意以下两点:

  • forward_list不提供size()成员函数。
  • forward_list没有指向最末元素,因此不提供back()、push_back()和pop_back()。

    这里写图片描述

 2.4 双端队列deque

deque属于段连续空间,

dequevectorlist
空间结构段连续空间连续空间不连续空间
功能比较

1.随机位置插入/删除效率不高(数据量512)

2.支持随机访问[比vector慢,因为要做堆跳转(迭代器结构复杂,会降低访问效率)

3.支持头添加,支持尾添加

1.随机位置插入/删除效率低

2.随机访问效率高(下标运算)

3.不支持头添加,支持尾添加

1.随机位置插入/删除效率高

2.不支持随机访问

支持头添3.加,支持尾添加

使用选择随机访问+头添加,就选deque,支持随机访问,即支持[]以及at(),但是性能没有vector好,可以在内部进行插入和删除操作,但性能不及list随机访问操作频率高,就选vector插入删除频率高,头尾添加,就选list
行为对比没有capacity和reserve有capacity和reserve没有capacity和reserve

 2.5 map 

map属于一种关联容器,它保存的是一个键值,通过键值来排序,通过键值来查找访问。Map使用的是平衡二叉树结构

头文件#include <map>
using namespace std;
语法格式

map<xx , xx>mp

<>可以放基本数据类型、结构体、指针、对象

键值对,有序,平衡二叉树
构造函数map()无参数的构造
map(const _map& _Right);一个参数
map(InputIterator _First, InputIterator _Last);另一个对象的一段
定义map的迭代器map<int,char>::iterator ite;

begin()  指向头

end()    指向尾元素的下一个元素,不能进行加法运算,可以自加++

first指向键值

second指向实值

 
空间属性
 
empty()对象是否有元素
size()元素个数
resize()

重新设置元素个数

缩小时大小改变  容量不变,

放大时大小改变  容量改变

 count(const Key& _Key)得到某一个元素数量,或者说判断一个键值是否存在
没有capacity(),reserve(),因为是链表

查寻

注:相对于数组来讲查询速度慢(因为需要遍历查询)

输出全部元素

for(ite; ite != mp.end(); ite++)

    {

        cout<<ite->first<<ite->second<<endl;

    }

输出单个元素: 

 cout<<ite->first<<ite->second<<endl;

iterator find( const Key& _Key);有就返回所在节点迭代器,没找到则无法输出,崩溃

增加

注:插入效率低于链表,因为涉及到排序

pair <iterator, bool> insert( const TYPE &val );pair<键,值>,键值不能重复,实值可以重复。输出first和second

insert( iterator pos, const TYPE &val );

迭代器可以++,但是不可以+4
void insert( iterator loc, input_iterator start, input_iterator end );在某个迭代器后加入另一个向量的中间一段
删除iterator erase( iterator _Where);迭代器指定的删除
iterator erase(iterator _First,iterator _Last);迭代器指定的段删除
size_type erase( const key_type& _Key);根据键值删除
void clear();删除所有
void remove( const TYPE &val );删除所有跟参数相同的元素。如果是结构体需要重载==
void unique(void) 删除list中重复的元素 
修改(键值不能改,实值可以改)利用迭代器修改
赋值函数assign
赋值=修改
交换swap()
lower_bound(key)返回参数key位置,该位置的键值不小于key : key<=pos
upper_bound(key)返回位置,该位置的键值>key  : key < pos
equal_range(key)返回这个区间
无序容器(散列表)unorder_map\unorder_multimap\unorder_set\unorder_multiset
hash_map哈希表 头文件<hash_map>

3.几种简单算法

包含头文件#include <algorithm>

3.1排序

3.1.1  从小到大

template<class RandomAccessIterator>
void sort(RandomAccessIterator _First, RandomAccessIterator _Last );

示例:

void fun(int c)
{
    cout<< c <<endl;
}
void vector_sort()
{
    vector<int>vec(5);
    vec.at(0) = 5;
    vec.at(1) = 1;
    vec.at(2) = 2;
    vec.at(3) = 7;
    vec.at(4) = 4;

    sort(vec.begin(), vec.end());  //从小到大
    for_each(vec.begin(), vec.end(), fun);
}

3.1.2 从大到小

template<class RandomAccessIterator, class Pr>
void sort( RandomAccessIterator _First,  RandomAccessIterator _Last, BinaryPredicate _Comp);

其中参数三 greater<>() 可以指定从大到小

示例:

void fun(int c)
{
    cout << c <<endl;
}
void vector_sort()
{
    vector<int>vec(5);
    vec.at(0) = 5;
    vec.at(1) = 1;
    vec.at(2) = 2;
    vec.at(3) = 7;
    vec.at(4) = 4;

    sort(vec.begin(), vec.end(), greater<int>());  //从大到小
    for_each(vec.begin(), vec.end(), fun);
}

3.1.3  随机排序

void random_shuffle(RandomAccessIterator _First, RandomAccessIterator _Last );

示例:

void vector_random_shuffle()
{
    vector<int>vec(5);
    vec.at(0) = 5;
    vec.at(1) = 1;
    vec.at(2) = 2;
    vec.at(3) = 7;
    vec.at(4) = 4;

    sort(vec.begin(), vec.end());  //从小到大
    for_each(vec.begin(), vec.end(), fun);

    srand(time(0));
    random_shuffle(vec.begin(), vec.end()); //利用随机数发生器,涉及到srand-随机种子
    for_each(vec.begin(), vec.end(), fun);
}

3.2 遍历(三种for循环)

3.2.1 常规for循环( 在复杂的逻辑中效率高

void look_vector_output_all()
{
    vector<int>vec(5,10);
    vec.at(4) = 15;
    vector<int>::iterator ite = vec.begin();
    vector<int>::iterator ite1 = vec.end();

    for(ite; ite != vec.end(); ite++)
    {
        cout<< *ite<<endl;
    }

}

3.2.2  简洁fore循环(简洁易用,不容易发生错误,适用于数组、集合遍历循环未知的情况下是不能进行增删操作的

void look_vector_output_all()
{
    vector<int>vec(5,10);
    vec.at(4) = 15;
    vector<int>::iterator ite = vec.begin();
    vector<int>::iterator ite1 = vec.end();

    for(auto vec_temp : vec)
    {
        cout<<vec_temp<<endl;
    }

}

3.2.3 for_each循环

  • foreach循环的非简化版,在C++11之前是没有auto的。
  • for_each的重载形式支持多线程优化
  • for_each更适合和c++20中的range搭配
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用 for_each 算法结合 lambda 表达式遍历容器中的元素
    std::for_each(numbers.begin(), numbers.end(), [](int number){
        std::cout << number << " ";
    });

    return 0;
}

4.迭代器

迭代器相当于char *,使用迭代器更重要的一点原因是与算法相结合,并且适用于所有容器

常规用法 :

  • string:string::ierator ite;
  • vector:vector<int>::iterator ite;
  • list:list<Node>::iterator ite;
  • for (ite = str.begin();ite != str.end(); ite++)
    {
        cout << *ite;
    }

注意:end()是末尾元的指向的下一个元素(NULL)

 迭代器失效

原因:迭代器在创建完毕对象后指向对象首地址,当对象发生改变后,一定是重新申请空间。但此时迭代器指向没有发生改变,会导致迭代器失效。 因此如果对象发生了内存变化,一定要重新调用一遍迭代器。

容器类型

行为

迭代器是否失效
以string为例str.append():数据追加到原字符串尾部,当初始化空间不够的时候,会重新开辟新的空间将所有数据搬运到新空间失效
str.insert():数据插入到原字符串指定位置,当初始化空间不够的时候,会重新开辟新的空间将所有数据搬运到新空间失效
str.erase(): 删除数据,但是初始地址不会改变                不失效
str.assign():重新赋值,初始地址不会改变不失效

如果是数组,string等由空间容量的容器,当空间容量满的时候系统会重新分配空间,此时迭代器会失效。

如果是链表、Map,不存在空间容量之说,迭代器不会失效

5.适配器

6.分配器

7.仿函数

                                                                  后续更新

   

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值