1.STL介绍
1.1概念(标准模板库
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。
1.2STL版本问题(主流有4个)
- 原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些 代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。
- P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改
- RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
- SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和 编程风格上看,阅读性非常高。后面学习STL要阅读部分源代码,主要参考的就是这个版本
3. STL的六大组件
- 容器(Containers):STL提供了多种容器,包括数组(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等。这些容器提供了不同的数据结构,以满足各种不同的需求。
- 算法(Algorithms):STL包含了大量的常用算法,如排序、查找、遍历等,这些算法可以用于各种容器,使得对数据的处理变得非常方便。
- 迭代器(Iterators):迭代器是STL中用于遍历容器中元素的工具,它提供了一种统一的访问容器元素的方式,使得算法能够适用于不同类型的容器。
- 仿函数(Functors):仿函数是一种类对象,它重载了函数调用操作符(),使得可以像函数一样调用这个类对象。STL中的很多算法都可以接受仿函数作为参数,以实现更加灵活的功能。
- 适配器(Adapters):STL提供了一些适配器,如栈(stack)、队列(queue)、优先队列(priority_queue),它们是基于其他容器实现的高层次数据结构,提供了特定的操作接口。
- 分配器(Allocators):分配器用于管理内存分配和释放,STL提供了一些标准的分配器,同时也允许用户定义自己的分配器,以满足特定的内存管理需求。
2.string类的基本介绍
在 C 语言中,字符串是以 null (也就是"/0")结尾的字符数组,需要手动管理内存和处理字符串操作。string.h 头文件提供了一系列库函数,如 strlen、strcpy、strcat 等,用于对字符串进行操作。但是这些函数的确与字符串是分离的,需要手动管理内存,容易出现越界访问等问题。
而在 C++ 标准库中,提供了 std::string 类,它封装了字符串的操作,提供了丰富的成员函数和运算符重载,使得字符串的操作更加方便和安全。std::string 类封装了字符串数据和长度,隐藏了内存管理的细节,提供了自动扩容、内存管理、异常安全性等功能,大大简化了字符串的操作
总结:
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
实际上,std::string 是 C++ 标准库中的一部分,而 STL(标准模板库)是 C++ 标准库的子集,但是由于它和其他 STL 容器(如 std::vector、std::list)有着相似的使用方式,因此可以将其放在一起学习和使用(出现了.length()和.size()计算长度的原因,一个是自带的,一个是为了与其他容器相配)
3.string在底层实际是:basic_string模板类的别名 typedef basic_string<char, char_traits, allocator> string
,是 basic_string 类模板使用字符类型 char 实例化得到的一个类
4. 不能操作多字节或者变长字符的序列。
5.在使用string类时,必须包含#include头文件以及using namespace std;
-
我们使用string进行实例化时不用显示实例化,因为本身就是
basic_string<char>
3. string类对象的构造(构造函数)
#include<iostream>
using namespace std;
int main()
{
string s1;
string s2("abcd");
string s3(5, 'c');
string s4(s2);
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
return 0;
}
4.访问及遍历操作
4.1operator[ ] ([ ]加下标
重载了下标操作符
[]
,使得可以像访问数组一样使用下标来访问字符串中的单个字符,同时我们要注意返回值是char&
,我们也可以进行更改等其他操作
#include <climits>
#include <iostream>
#include <iterator>
using namespace std;
int main()
{
string s1("abcdef");
for (size_t i = 0; i < s1.size(); i++)//遍历
{
cout << s1[i] << " " ;
}
cout << endl;
s1[0] = '1';//修改
cout << s1 << endl;
return 0;
}
4.2基于范围for
int main()
{
string s1("abcdef");
//遍历
for (auto a: s1)//a代表的是命名,可以随便取
{
a = '1';
cout << a << " ";
}
//这里面只是拷贝,a的改变不会影响s1
cout << endl;
cout << s1 << endl;
//但是加上引用之后,a的改变会影响s1
for (auto& a : s1)
{
a = '2';
cout << a << " ";
}
cout << endl;
cout << s1 << endl;
return 0;
}
4.3使用迭代器(最推荐使用)
在 C++ 标准库中,
std::string
类提供了迭代器,用于遍历字符串中的字符。std::string
类的迭代器类型为std::string::iterator
,它是随机访问迭代器,支持随机访问操作(还没有正式讲到它,大家现在就把他当指针)
int main()
{
string s1("abcdef");
//迭代器
string::iterator a1 = s1.begin();//这相当于下标为0的地址
while (a1 != s1.end())//s1.end()相当于数组最后一位的下一位
{
cout << (*a1);
++a1;
}
return 0;
}
我们还是更倾向于使用迭代器:
- 大多数的数据结构都有,但是
[]
不是都有的
-
- 而且使用
[]
要求物理底层是连续的
- 而且使用
- 迭代器能跟算法一起用,下面给大家看看结合了
reverse()
(逆置)的使用
#include <climits>
#include <iostream>
#include <iterator>
#include<list>
using namespace std;
int main()
{
string s1("abcdef");
//迭代器
string::iterator a1 = s1.begin();//这相当于下标为0的地址
while (a1 != s1.end())//s1.end()相当于字符串最后一位的下一位
{
cout << (*a1);
++a1;
}
cout << endl;
reverse(s1.begin(), s1.end());//反转字符串
auto a2 = s1.begin();
while (a2 != s1.end())
{
cout << (*a2);
++a2;
}
cout << endl;
//迭代器还可以在链表中使用
list<double> l1;//定义一个链表来说明,迭代器两个特点
//尾插
l1.push_back(1.1);
l1.push_back(2.2);
l1.push_back(3.3);
list<double>::iterator lit1 = l1.begin();
while (lit1 != l1.end())
{
cout << *lit1 << " ";
lit1++;
}
cout << endl;
reverse(l1.begin(), l1.end());
auto lit2 = l1.begin();
while (lit2 != l1.end())
{
cout << *lit2 << " ";
lit2++;
}
cout << endl;
return 0;
}
5.string的迭代器(Iterator)
5.1介绍
迭代器是一种数据类型。在C++中,迭代器实际上是一种对象,它被设计用于在容器中进行元素的遍历和访问。迭代器为程序员提供了一种抽象的方式来访问容器中的元素,而不用关心容器的底层实现细节
std::string
类提供了多种类型的迭代器,包括正向迭代器(iterator)、常量正向迭代器(const_iterator)、反向迭代器(reverse_iterator)和常量反向迭代器(const_reverse_iterator)
- 正向迭代器(iterator):std::string::iterator 类型是用于遍历可修改字符串的迭代器,可以通过 begin() 和 end() 方法获取范围
- 常量正向迭代器(const_iterator):std::string::const_iterator 类型是用于遍历不可修改字符串的迭代器,可以通过begin() 和end() 方法获取范围。
- 反向迭代器(reverse_iterator):std::string::reverse_iterator 类型是用于以反向顺序遍历可修改字符串的迭代器,可以通过 rbegin() 和 rend() 方法获取范围
- 常量反向迭代器(const_reverse_iterator):std::string::const_reverse_iterator 类型是用于以反向顺序遍历不可修改字符串的迭代器,可以通过 crbegin() 和 crend() 方法获取范围
5.2 begin()和end()(正向和常正向
begin()返回指向容器中第一个元素的迭代器
这是一个重载:
iterator begin();
用于非常量对象,它返回一个迭代器,可以用于修改容器中的元素(可读可写)。const_iterator begin() const;
用于常量对象,它返回一个常量迭代器,用于指向容器中的元素,不允许修改容器中的元素(只读)
int main()
{
string s1("abcdef");
const string s2("ertyuio");//常量,不能修改
string::iterator a1 = s1.begin();
string::const_iterator a2 = s1.end();//权限缩小
string::const_iterator b1 = s2.begin();
//string::iterator b2 = s2.end();这种会报错,属于权限扩大
return 0;
}
end()用于返回指向容器中最后一个元素之后位置的迭代器
也是两个重载,与begin()一样
用法参考上面begin()
5.3rbegin()和rend()(反向和常反向)
rbegin
函数返回一个反向迭代器,指向容器中最后一个元素。反向迭代器允许从容器的末尾向前遍历容器中的元素。
rend
函数返回一个反向迭代器,指向容器中第一个元素之前的位置。通常用于标记反向遍历的结束位置。这两个也都有重载两个:反向和常量反向
6.string类对象的容量操作
6.1size和length
二者作用其实完全相同: 返回字符串有效字符长度
为什么有两个也是历史原因了.这里推荐大家经常用size(),好与后面联系起来
int main()
{
string s1("abcde");
cout << s1.size() << endl;
cout << s1.length() << endl;
return 0;
}
6.2capacity
capacity()
是 C++ 中std::string
类的一个成员函数,用于返回当前字符串对象分配的存储空间大小(即容量)。字符串对象的容量指的是在不重新分配内存的情况下,字符串可以存储的最大字符数
- 函数:
size_t capacity() const noexcept;
- 返回值: 一个无符号整数,表示当前字符串对象分配的存储空间大小
注意事项:
capacity()
返回的是字符串对象分配的总空间,而不是当前字符串的实际长度。- 在执行字符串操作后,
capacity()
返回的值可能会大于size()
返回的值,因为size()
表示实际存储的字符数,而capacity()
表示分配的总空间
int main()
{
string s1("abcde");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
6.3 reserve和rsize
1.reserve()
- 函数:
void reserve(size_t n)
- 功能说明: 用于为字符串预留至少
n
个字符的存储空间,即提前分配足够的空间,但并不改变字符串的实际大小。如果n
大于当前容量,reserve
可能导致内存重新分配,否则,它只是更新容量而无需重新分配内存
注意事项:
reserve
不影响字符串的实际大小,即size()
的值不会改变。- 如果
n
大于当前容量,reserve
可能会导致重新分配内存,但并不会初始化新分配的部分
- n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n;
- n 小于等于原字符串的 capacity(会不会缩容,看不同编译器的具体实现)
2.resize()
- 函数签名:
void resize(size_t n, char c = char());
或者void resize (size_t n);
- 功能说明: 将字符串的大小调整为
n
,并根据需要插入或删除字符,使得字符串的实际大小等于n
。如果n
小于当前大小,多余的字符将被删除;如果n
大于当前大小,字符串将被扩展,并使用字符c
填充新增的部分; 要是不写就是null characters(\0)
注意事项:
resize
会修改字符串的实际大小,即size()
的值会变为n
。(可以缩小,同时也删除了)- 如果
n
大于当前大小,新增的部分将用字符c
填充;要是不写就是null characters(\0)
- n 小于原字符串的 size,此时 resize 函数会将原字符串的 size 改为 n,也会改变字符串(删除),但不会改变 capacity
- n 大于原字符串的 size,但小于其 capacity,此时 resize 函数会将 size 后面的空间全部设置为字符 c
- n 大于原字符串的 capacity,此时 resize 函数会将原字符串扩容,然后将size 后面的空间全部设置为字符 c
7.string类对象的修改操作(+=,insert,erase)
7.1重载的+=(最常用的尾插)
- 函数:
string& operator+=(const string& str);
- 功能说明: 用于将当前字符串与另一字符串
str
进行连接,即将str
的内容附加到当前字符串的末尾
int main()
{
string s1("abcde");
s1 += "111";
cout << s1 << endl;
return 0;
}
7.2insert(效率不是很好)
前者是,在
pos
这个下标前插入str
字符串后者是,在
pos
下标前插入n
个c
int main()
{
string s1 = "abc";
cout << s1 << endl;
s1.insert(0, "111");//0前面不就相当于头插嘛
cout << s1 << endl;
s1.insert(0, 2, 'x');
cout << s1 << endl;
return 0;
}
各种详细的用法:
int main()
{
std::string str = "to be question";
std::string str2 = "the ";
std::string str3 = "or not to be";
std::string::iterator it;
//在pos数字(下标)前插入str2
cout << str.insert(6, str2) << endl; // to be (the )question
//在pos数字(下标)前插入str3字符串中下标为3的4个字符
cout << str.insert(6, str3, 3, 4) << endl;; // to be (not )the question
str.insert(10, "that is cool", 8); // to be not (that is )the question
str.insert(10, "to be "); // to be not (to be )that is the question
str.insert(15, 1, ':'); // to be not to be(:) that is the question
it = str.insert(str.begin() + 5, ','); // to be(,) not to be: that is the question
str.insert(str.end(), 3, '.'); // to be, not to be: that is the question(...)
str.insert(it + 2, str3.begin(), str3.begin() + 3); // (or )
std::cout << str << '\n';
return 0;
7.3 erase(任意位置删除)
删除从下标
pos
处开始算起的len
个字符
int main()
{
string s1 = "abc";
cout << s1 << endl;
s1.insert(0, "111");//0前面不就相当于头插嘛
cout << s1 << endl;
s1.insert(0, 2, 'x');
cout << s1 << endl;
s1.erase(0, 5);//从0处,删除5个
cout << s1 << endl;
return 0;
}
补充:npos
npos
是 C++ 中std::string
类的一个静态成员变量,表示无效或不存在的位置。通常用于标识字符串查找等操作未找到匹配项的情况。npos
的类型是size_t
,它是一个无符号整数类型npos一般是指一个非常大的正数
8.String operations函数(find,rfind,substr)
8.1find
find 用于返回 一个字符或一个字符数组或一个string对象 在 string 中首次出现的位置(返回下标),如果找不到就返回 npos
8.2rfind
整体跟
find
类似。从后往前找,找到一个字符或一个字符数组或一个string对象最后一次出现的位置,如果找不到就返回 npos
8.3substr(截取字符串)
从
pos
处开始截取len
长度(默认的话,截取到最后)
int main()
{
string str = "We think in generalities, but we live in details.";
string str2 = str.substr(3, 5); // "think"
cout << str2 << endl;
size_t pos = str.find("live"); // pos='l'的下标
string str3 = str.substr(pos); // ”live in details.“ -----后面什么也不给,使用缺省值
cout << str3 << endl;
return 0;
}