学好STL
一.STL简介(了解)
1.什么是STL
STL(standard template libaray-标准模板库): 是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且
是一个包罗数据结构与算法的软件框架。,是一个高效的c++程序库
2.STL的六大组件
组件名称 | 说明 |
---|---|
Container(容器) 各种基本数据结构 | 向量(vector)、双端队列(deque)、列表(list)、集合(set)、多重集合(multiset)、映射(map)和多重映射(multimap),string |
Adapter(适配器) 可改变containers、Iterators或Function object接口的一种组件 | 为已有的类提供新的接口,目的是简化、约束、使之安全、隐藏或者改变被修改类提供的服务集合 |
Algorithm(算法) 各种基本算法如sort、search…等 | 算法Algorithms,用来处理群集内的元素。它们可以出于不同的目的而搜寻、排序、修改、使用那些元素。通过迭代器的协助,我们可以只需编写一次算法,就可以将它应用于任意容器,这是因为所有的容器迭代器都提供一致的接口 |
Iterator(迭代器) 连接containers和algorithms | 1.迭代器Iterators,用来在一个对象群集(collection of objects)的元素上进行遍历。这个对象群集或许是个容器,或许是容器的一部分。迭代器的主要好处是,为所有容器提供了一组很小的公共接口。迭代器以++进行累进,以*进行提领,因而它类似于指针 |
Function object(函数对象) | 1、一个行为类似函数的对象,它可以没有参数,也可以带有若干参数。2.重载了调用运算符operator()的类的对象都满足函数对象的特征 3、STL中也定义了一些标准的函数对象,如果以功能划分,可以分为算术运算、关系运算、逻辑运算三大类。为了调用这些标准函数对象,需要包含头文件 |
Allocator(分配器) | 负责空间配置与管理。从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。 |
3.STL的缺陷
- STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出
来已经相隔了13年,STL才进一步更新。 - STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
- STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
- STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语
法本身导致的。
2.string
1.string的简单了解
1. string是表示字符串的字符串类
-
string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类
-
string在底层实际是: basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string;
如何对stl的查阅
对stl的学习,我们需要掌握30%左右的接口函数,剩下的接口函数很少用,不需要记住,当我们需要去使用时,再去查阅,那么我们应该怎么去查阅这些接口函数,首先,我们给出两个网站:
一个是c++官网:https://en.cppreference.com/w/,它支持最新的语法,但它的界面比较乱
另一个是:http://www.cplusplus.com/reference/string/string/string/,它不是官网,它支持c++98和c++11,它的界面比较整洁和规范,容易看,所以推荐使用这个网站去查阅stl。下面我也会介绍怎样去查阅。下面我们以查string为例子:
然后我们随便点一个接口函数:
2. string常用接口说明
1.string类 对象常见的构造
2.string类对象的容量操作
void test
{
string s1("hello world");
cout << s.size() << endl;//输出11
cout << s.capacity() << endl;//输出31
s1.resize(20, 'x');//直接在hello world接9个x,size和capacity都会改变
s1.resize(5);//只保留前5个字符,但capacity不变
s1.clear();//将s1中的所有字符全部清空,将size清空为0,不改变capacity的大小
string s2;
s2.resize(20);//将s2中的有效字符增加到20个,为20个'\0'
string s3;
s3.reserve(20);//将capacity的大小改变为20,不改变size;
}
总结;
-
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size(); -
clear()只是将string中有效字符清空,不改变底层空间大小。
-
resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变。 -
reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
3. string类对象的访问及遍历操作
总结:
1.[begin(),end()) end()返回的是最后一个字符的下一个位置,c++中凡是给迭代器一般都是给的[)左闭右开的区间。
2.迭代器是类似指针一样的东西,迭代器以++进行累进,以*进行提领。
3.迭代器的意义:像string,vector支持[]遍历,但是list,map等等容器不支持[],迭代器能够使遍历的方式统一
4.迭代器是可读可写。const_iterator const对象 只能读不能写,const对象只能用const_iterator
4. string类对象的修改操作
void test
{
string s1;
//尾插一个字符
s1.push_back('h');
s1.push_back('e');
s1.push_back('l');
s1.push_back('1');
s1.push_back('o');
cout << s1 << endl;//输出hello
//尾插一个字符串
s1.append("world");
cout << s1 << endl;//输出helloworld
//尾插一个字符串
s1.append(s2);//或者s1.append(s2.begin(), s2.end());
cout << s1 << endl;//输出helloworld!!!
//尾插实际喜欢用+=
s1 += ' ';
s1 += "sjp";
s1 += s2;
cout << s1 << endl;//输出helloworld!!! sjp!!!
//插入数据
s1.insert(4," ");//在位置4插入一个空格
cout << s1 << endl;//输出hello world!!! sjp!!!
//插入不能越界,例如:
//s1.insert(300, "yyyy");会报错
//删除
s1.erase(0, 3);//从0位置开始,删除3个
//s1.erase(0, 1000);//删多了,不会报错,把字符串全部删完
s1.erase(3);//这个从3的位置后面全部删完
s1.erase();//这个全部删完
//假设要求取出文件名的后缀
string filename1("test.cpp");
size_t pos1 = filename1.find('.');//如果找不到,则返回npos
if (pos1 != string::npos)
{
string suff(filename, pos1);
cout << suff << endl;//输出cpp
}
string filename2("test.a.cpp");
size_t pos2=filename2.rfind('.');//从后往前开始找,找不到则返回npos
if (pos2 != string::npos)
{
string suff(filename, pos2);
cout << suff << endl;//输出cpp
}
}
获取一个网址的协议名和域名
string GetProtocol(const string& ur1)//获取协议名
{
size_t pos = ur1.find("://");//find不仅可以查找字符也可以查找字符串
if (pos != string::npos)
{
return ur1.substr(0, pos);//返回一个从0开始,pos个字符的字串
}
else
{
return string();//返回一个匿名对象,为空串
}
}
string GetDomain(const string& ur1)//域名
{
size_t pos = ur1.find("://");
if (pos != string::npos)
{
size_t start = pos + 3;
size_t end=ur1.find("/",start);//从start开始找”/"
if (end != string::npos)
{
return ur1.substr(start,end-start);//从start开始截取end-start个字符
}
else
{
return string();//返回一个匿名对象,为空串
}
}
}
int main()
{
string s1 = "https://daohang.qq.com/?fr=hmpage";
string s2 = "http://www.cplusplus.com/reference/string/string/string/";
cout<<GetProtocol(s1)<<endl;
cout<< GetDomain(s1) <<endl;
cout<<GetProtocol(s2)<<endl;
cout<<GetDomain(s2)<<endl;
return 0;
}
输出结果为:
注意:
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。 - 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
- 尽量少用insert,erase,因为底层实现是数组,头部或者中间要挪动数据,效率太低了。
感谢你的点赞和关注,收藏!!!!