上次介绍完了模版和泛型编程:C++初阶:入门泛型编程(函数模板和类模板)
今天开始stl
的内容了
STL (standard template libaray - 标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架
原始版本
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要阅读部分源代码,主要参考的就是这个版本
- 容器(Containers):STL提供了多种容器,包括数组(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等。这些容器提供了不同的数据结构,以满足各种不同的需求。
- 算法(Algorithms):STL包含了大量的常用算法,如排序、查找、遍历等,这些算法可以用于各种容器,使得对数据的处理变得非常方便。
- 迭代器(Iterators):迭代器是STL中用于遍历容器中元素的工具,它提供了一种统一的访问容器元素的方式,使得算法能够适用于不同类型的容器。
- 仿函数(Functors):仿函数是一种类对象,它重载了函数调用操作符
()
,使得可以像函数一样调用这个类对象。STL中的很多算法都可以接受仿函数作为参数,以实现更加灵活的功能。- 适配器(Adapters):STL提供了一些适配器,如栈(stack)、队列(queue)、优先队列(priority_queue),它们是基于其他容器实现的高层次数据结构,提供了特定的操作接口。
- 分配器(Allocators):分配器用于管理内存分配和释放,STL提供了一些标准的分配器,同时也允许用户定义自己的分配器,以满足特定的内存管理需求。
在 C 语言中,字符串是以 null 结尾的字符数组,需要手动管理内存和处理字符串操作。
string.h
头文件提供了一系列库函数,如strlen
、strcpy
、strcat
等,用于对字符串进行操作。但是这些函数的确与字符串是分离的,需要手动管理内存,容易出现越界访问等问题。而在 C++ 标准库中,提供了
std::string
类,它封装了字符串的操作,提供了丰富的成员函数和运算符重载,使得字符串的操作更加方便和安全。std::string
类封装了字符串数据和长度,隐藏了内存管理的细节,提供了自动扩容、内存管理、异常安全性等功能,大大简化了字符串的操作
总结:
string是表示字符串的字符串类
该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
实际上,
std::string
是 C++ 标准库中的一部分,而 STL(标准模板库)是 C++ 标准库的子集,但是由于它和其他 STL 容器(如std::vector
、std::list
)有着相似的使用方式,因此可以将其放在一起学习和使用(出现了.length()
和.size()
计算长度的原因,一个是自带的,一个是为了与其他容器相配)
string在底层实际是:basic_string模板类的别名 typedef basic_string
,是 basic_string 类模板使用字符类型 char 实例化得到的一个类
我们使用string进行实例化时不用显示实例化,因为本身就是basic_string
构造函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用 C 风格的字符串(以 null 结尾的字符数组)来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string& s) | 拷贝构造函数 |
#include
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;
}
重载了下标操作符
[]
,使得可以像访问数组一样使用下标来访问字符串中的单个字符,同时我们要注意返回值是char&
,我们也可以进行更改等其他操作。
int main()
{
string s1 = "abcde";//拷贝构造
for (int i = 0; i < s1.size(); ++i)//进行遍历
{
cout << s1[i] << " ";
}
cout << endl;
//进行更改
s1[0] = '1';
cout << s1;
return 0;
}
int main()
{
string s1 = "abcde";//拷贝构造
for (auto e: s1)//只能遍历
{
cout << e << " ";
}
cout << endl;
for (auto& e : s1)//想要改变,加&
{
e = 'a';
cout << e << " ";
}
cout << endl;
return 0;
}
在 C++ 标准库中,
std::string
类提供了迭代器,用于遍历字符串中的字符。std::string
类的迭代器类型为std::string::iterator
,它是随机访问迭代器,支持随机访问操作(还没有正式讲到它,大家现在就把他当指针)
int main()
{
string s1 = "abcde";//拷贝构造
string::iterator it = s1.begin();//迭代器1.大多数的数据结构都有,但是[]不是都有的
//2. 迭代器能跟算法一起用,下面就是结合了reverse的使用
while (it != s1.end())
{
cout << *it << " ";
it++;
}
return 0;
}
我们还是更倾向于使用迭代器:
[]
不是都有的[]
要求物理底层是连续的reverse()
(逆置)的使用int main()
{
string s1 = "abcde";//拷贝构造
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
reverse(s1.begin(), s1.end());//开始翻转
it = s1.begin();
while (it != s1.end())//再次打印
{
cout << *it << " ";
it++;
}
cout << endl;
list l1;//定义一个链表来说明,迭代器两个特点
l1.push_back(1.1);
l1.push_back(2.2);
l1.push_back(3.3);
list::iterator lit=l1.begin();
while (lit != l1.end())
{
cout << *lit << " ";
lit++;
}
cout << endl;
reverse(l1.begin(), l1.end());
lit = l1.begin();
while (lit != l1.end())
{
cout << *lit << " ";
lit++;
}
cout << endl;
return 0;
}
迭代器是一种数据类型。在C++中,迭代器实际上是一种对象,它被设计用于在容器中进行元素的遍历和访问。迭代器为程序员提供了一种抽象的方式来访问容器中的元素,而不用关心容器的底层实现细节
迭代器名称 | 功能说明 |
---|---|
begin() | 返回一个指向字符串中第一个字符的迭代器 |
end() | 返回一个指向字符串最后一个字符下一个位置的迭代器 |
rbegin() | 反向开始,返回一个指向字符串最后一个字符的迭代器 |
rend() | 反向开始,返回一个指向字符串中第一个元素之前的位置的迭代器 |
std::string
类提供了多种类型的迭代器,包括正向迭代器(iterator)、常量正向迭代器(const_iterator)、反向迭代器(reverse_iterator)和常量反向迭代器(const_reverse_iterator)
std::string::iterator
类型是用于遍历可修改字符串的迭代器,可以通过 begin()
和 end()
方法获取范围std::string::const_iterator
类型是用于遍历不可修改字符串的迭代器,可以通过 begin()
和 end()
方法获取范围。std::string::reverse_iterator
类型是用于以反向顺序遍历可修改字符串的迭代器,可以通过 rbegin()
和 rend()
方法获取范围std::string::const_reverse_iterator
类型是用于以反向顺序遍历不可修改字符串的迭代器,可以通过 crbegin()
和 crend()
方法获取范围反向用的少点
begin()返回指向容器中第一个元素的迭代器
这是一个重载:
iterator begin();
用于非常量对象,它返回一个迭代器,可以用于修改容器中的元素(可读可写)。const_iterator begin() const;
用于常量对象,它返回一个常量迭代器,用于指向容器中的元素,不允许修改容器中的元素(只读)
int main()
{
string s = "abcde";
const string cs = "abc";
string::iterator it = s.begin();//会去找最适合的,string::const_iterator it = s.begin();可以
//还是,权限不能放大,只能缩小
string::const_iterator cit = cs.begin();//用string::iterator cit = cs.begin();会报错
return 0;
}
end()用于返回指向容器中最后一个元素之后位置的迭代器
也是两个重载,与begin()一样
rbegin
函数返回一个反向迭代器,指向容器中最后一个元素。反向迭代器允许从容器的末尾向前遍历容器中的元素。
rend
函数返回一个反向迭代器,指向容器中第一个元素之前的位置。通常用于标记反向遍历的结束位置。这两个也都有重载两个:反向和常量反向
所以也能看到:后面四个虽然是c11新加的东西,但是没人用
函数名称 | 功能说明 |
---|---|
size()(重点) | 返回字符串有效字符长度 |
length() | 返回字符串有效字符长度 |
capacity () | 返回当前分给字符串空间总大小 |
empty() (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear() (重点) | 清空有效字符 |
reserve() (重点) | 为字符串预留空间(不同版本具体实现不同,没有硬性规定) |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
二者作用其实完全相同: 返回字符串有效字符长度
为什么有两个也是历史原因了.这里推荐大家经常用size(),好与后面联系起来
int main()
{
string s1("abcde");
cout << s1.size() << endl;
cout << s1.length() << endl;
return 0;
}
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;
}
void reserve(size_t n);
n
个字符的存储空间,即提前分配足够的空间,但并不改变字符串的实际大小。如果 n
大于当前容量,reserve
可能导致内存重新分配,否则,它只是更新容量而无需重新分配内存reserve
不影响字符串的实际大小,即 size()
的值不会改变。n
大于当前容量,reserve
可能会导致重新分配内存,但并不会初始化新分配的部分
- n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n;
- n 小于等于原字符串的 capacity(会不会缩容,看不同编译器的具体实现)
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
int main()
{
string s1("abcde");
s1.resize(10);
cout << s1.capacity() << endl;
cout << s1 << endl;
s1.resize(3);
cout << s1.capacity() << endl;
cout << s1 << endl;
return 0;
}
string& operator+=(const string& str);
str
进行连接,即将 str
的内容附加到当前字符串的末尾int main()
{
string s1("abcde");
s1 += "111";
cout << s1 << endl;
return 0;
}
前者是,在
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;
}
删除从下标
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
是 C++ 中std::string
类的一个静态成员变量,表示无效或不存在的位置。通常用于标识字符串查找等操作未找到匹配项的情况。npos
的类型是size_t
,它是一个无符号整数类型
find 用于返回 一个字符或一个字符数组或一个string对象 在 string 中首次出现的位置(返回下标),如果找不到就返回 npos
整体跟
find
类似。从后往前找,找到一个字符或一个字符数组或一个string对象最后一次出现的位置,如果找不到就返回 npos
从
pos
处开始截取len
长度(默认的话,截取到最后)
int main()
{
string s1("test.c");
//得到文件后缀
size_t pos1 = s1.find('.');
if (pos1 != string::npos)
{
string ret1 = s1.substr(pos1);
cout << ret1 << endl;
}
//如果文件名含有 . 怎么办
string s2("te.st.c");
size_t pos2 = s2.rfind('.');
if (pos2 != string::npos)
{
string ret2 = s2.substr(pos2);
cout << ret2 << endl;
}
return 0;
}
好啦,这次就先到这里啦,以后再遇到其他的接口会继续给大家整理的。感谢大家支持!!!!