STL(初识string)

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的

组件库,而且是一个包罗数据结构与算法的软件框架。

STL有六大组件构成,核心部分是容器和算法两部分

stl最主要的是学习理解,能够自己仿实现stl的功能。

string

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列

的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户

自己管理,稍不留神可能还会越界访问

在使用string类时,必须包含**#include头文件** 以及using namespace std;

string的成员函数了解

string是个可动态镇长的字符数组,下面是他默认的成员函数

1:构造 / 析构 / 赋值,老生常谈的东西了。这里就不详细展开解释了

2:迭代器相关成员函数

|-----------|--------------------------------------------------------------|
| begin | 返回指向容器起始位置的普通迭代器,可用于修改元素。 |
| end | 返回指向容器末尾位置(最后一个元素的下一个位置)的普通迭代器。 |
| rbegin | 返回指向容器反向起始位置(即普通末尾位置)的反向迭代器,用于反向遍历。 |
| rend | 返回指向容器反向末尾位置(即普通起始位置的前一个位置)的反向迭代器。 |
| cbegin | 返回指向容器起始位置的常量迭代器const_iterator),仅用于读取元素,不可修改。 |
| cend | 返回指向容器末尾位置的常量迭代器。 |
| crbegin | 返回指向容器反向起始位置的常量反向迭代器const_reverse_iterator),仅用于读取元素。 |
| crend | 返回指向容器反向末尾位置的常量反向迭代器。 |

3:容量管理相关的成员函数列表

|-------------------|----------------------------------------------------------------------------|
| size / length | 两者功能完全相同,返回字符串的有效字符长度 (不包含结尾的 \0)。 |
| max_size | 返回字符串理论上能达到的最大长度(受系统内存限制,一般仅作参考)。 |
| resize | 调整字符串的长度:若新长度大于原长度,会用指定字符(默认 \0)填充;若小于原长度,会截断多余字符。 |
| capacity | 返回字符串当前已分配的内存容量 (即实际占用的内存大小,通常大于等于 size)。 |
| reserve | 预分配内存容量:若传入的容量大于当前 capacity,会扩容到该容量;若小于,则无操作(不缩容)。常用于提前预留内存,减少后续扩容的性能开销。 |
| clear | 清空字符串,使其长度变为 0(但已分配的内存容量 capacity 不变)。 |
| empty | 判断字符串是否为空(长度为 0 时返回 true,否则返回 false)。 |
| shrink_to_fit | (C++11 新增)将已分配的内存容量收缩到与有效长度 size 一致,用于释放多余的内存空间。 |

4:元素访问相关的成员函数列表

|--------------|--------------------------------------------------------|
| operator[] | 通过下标访问字符串中的字符,不做越界检查(若下标非法,行为未定义)。 |
| at | 通过下标访问字符串中的字符,会做越界检查 (若下标非法,抛出 out_of_range 异常)。 |
| back | (C++11 新增)返回字符串的最后一个字符(若字符串为空,行为未定义)。 |
| front | (C++11 新增)返回字符串的第一个字符(若字符串为空,行为未定义)。 |

5:修改操作相关的成员函数列表

|--------------|----------------------------------------------------|
| operator+= | 字符串拼接,将右侧字符串追加到当前字符串末尾。 |
| append | 字符串拼接,功能与 operator+= 类似,支持更灵活的拼接形式(如拼接子串、多个字符等)。 |
| push_back | 向字符串末尾追加一个字符。 |
| assign | 给字符串赋值,可替换原有内容(支持赋值字符串、子串、多个字符等形式)。 |
| insert | 在字符串的指定位置插入字符或子串。 |
| erase | 删除字符串中指定位置的字符或子串。 |
| replace | 替换字符串中指定范围的子串为新内容。 |
| swap | 交换两个字符串的内容。 |
| pop_back | (C++11 新增)删除字符串的最后一个字符。 |

6:字符串操作相关的成员函数列表

|---------------------|--------------------------------------------------------------------------------|
| c_str | 返回与 string 等价的 C 风格字符串(以 \0 结尾的 char*),用于兼容 C 语言接口。 |
| data | 返回字符串的字符数据指针(const char*),功能与 c_str 类似,但不保证以 \0 结尾(实际中多数实现与 c_str 一致)。 |
| get_allocator | 获取 string 内部使用的内存分配器(一般用于底层内存管理,业务代码很少用到)。 |
| copy | 将字符串的字符序列拷贝到指定的字符数组中。 |
| find | 从前往后查找子串或字符,返回首次出现的位置;若未找到,返回 string::npos。 |
| rfind | 从后往前查找子串或字符,返回最后一次出现的位置;若未找到,返回 string::npos。 |
| find_first_of | 查找字符串中首次出现的目标字符集中的任意字符,返回其位置。 |
| find_last_of | 查找字符串中最后一次出现的目标字符集中的任意字符,返回其位置。 |
| find_first_not_of | 查找字符串中首次不出现目标字符集中任意字符的位置。 |
| find_last_not_of | 查找字符串中最后一次不出现目标字符集中任意字符的位置。 |
| substr | 从指定位置开始,截取指定长度的子串并返回。 |
| compare | 按字典序比较两个字符串,返回 0(相等)、正数(当前字符串更大)或负数(当前字符串更小)。 |

这里简单的记一下,这里有个比原网址还不错的网站:string - C++ Reference

string使用

注:这是c++98版本的string

在(3)里的len = npos,是已有缺省值,npos=-1(是str const里应该静态成员变量,在外使用得用string::npos。-1是取最大值),在我们不传入对应参数时会自动全部拷贝

一、构造

string()

cpp 复制代码
int main()
{
    string s1;//默认构造
    string s2("hello");//带参构造
    string s3(s2);//拷贝构造
    string s4(s2, 2, 3);//子串构造,从第二个位置开始截取3个字符(如果截取字符大于已有字符,截取完直接结束)
    string s5("World", 3);//从\0结尾的字符串取(也叫做C风格字符串)的前3个字符构造。 
    string s6(5, '*');//用5个'*'构造
     string s7(s2.begin(), s2.begin() + 3);//通过迭代器范围构造(以s2为例,取从begin到begin+3的字符)
    //支持流插入输出
    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
    cin >> s1;
    cout << s1 << endl;
    return 0;
}
operatoe[]函数:

string重载了operatoe[],可以像数组一样直接修改任意的内容:

cpp 复制代码
//逻辑演示重载了operatoe[],并非完整代码
class string
{
public:
    char& operator[](size_t i)
    {
        return _str[i];
    }
private:
    char* _str;//tring底层大概就是这样,指针、存字符串、开空间
    size_t _size;
    size_t _capacity;
};

int main(){
     string s2("hello");
     string s2[0] = 'x';//直接修改指定位置
}
size函数

早期开发时,使用的是length,但发现有局限,在数里用length不合理,于是出现个size,比length更有通用性,常用。

size接口可以让我们获取字符长度:

cpp 复制代码
void demo_string3()
{
    string s2("hello world");
    cout << s2.length() << endl;
    cout << s2.size() << endl;
}

这里可以用于读取打印数组存储的字符串:

cpp 复制代码
void demo_string1()
{
    string s1;
    string s2("hello world");

    cout << s1 << s2 << endl;

    s2[0] = 'x';
    cout << s1 << s2 << endl;

    for (size_t i = 0; i < s2.size(); i++)
    {
        cout << s2[i] << " ";//调用size打印
    }
    cout << endl;
}
int main(){
    demo_string1();
    }
迭代器string::iterator:
cpp 复制代码
void demo_string1()
{
    string s1;
    string s2("hello world");
    const string s3("hello world");

    cout << s1 << s2 << endl;

    s2[0] = 'x';
    cout << s1 << s2 << endl;

   string::iterator it = s2.begin();
    while (it != s2.end())
    {
    //*it +=2;//iterator迭代器可以被修改,按照字母表往后推两个字母
    cout << *it << " ";
    ++it;
    }

    string::const_iterator it = s3.begin();//const_iterator是实现const的迭代器,指向内容不能修改,但依然可以++it。begin()也会自动调用const版本,限制到只读范围
    while (it != s3.end())
    {
    cout << *it << " ";
    ++it;
    }
cout << endl;
}
int main(){
    demo_string1();
    }

string::iterator it = s2.begin();定义一个迭代器it( 根据编译器的不同底层实现可能是或不是指针),并让它指向s2的起始位置。

while里,it从s2的begin()(第一个字符)开始,直到遇到end()(最后一个字符)停止。

begin()自带const版本,会判断是普通、const迭代器,选用对应版本。const版本限制的是const int*而不是int* const。

const迭代器不是加了const的迭代器(conststring::const_iterator it = s3.begin();)。

iterator 不只支持数组,还提供了**统一的容器访问方式,**是个很重要的函数。

反向迭代器string::reverse_iterator:
cpp 复制代码
void demo_string2()
{
    string s2("hello world");
    string::iterator it = s2.begin();
    while (it != s2.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
//反向迭代器
    string::reverse_iterator rit = s2.rbegin();//理解指向最后一个字符
    while (rit != s2.rend())//rend可以理解指向第一个字符
    {
        cout << *rit << " ";
        ++rit;//reverse_iterator 倒着走,++就是往前走
    }
    cout << endl;
}

反向迭代器的逻辑是倒着从后往前遍历,所以使用的还是++rit。这里明确的一点是,反向迭代器不是原生的指针,而是一个封装的类,只有重载了运算符,才能让++倒着走。

这里rbegin()也通begin()一样,自带const版本。

auto与范围for

以下为C++十一提供的访问容器方式,叫做范围for

范围for,for循环后的括号由冒号:分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。自动迭代,自动取数据,自动判断结束。

auto,自动推导。自动赋值迭代,虽然没用写明,但底层依然是迭代器原理。

范围for适合和auto搭配使用。

cpp 复制代码
auto = &x//后面是啥类型auto是啥类型
auto* = &x //auto是指针
ayto& = x //auto是引用
cpp 复制代码
for (auto ch : s2)
{
    cout << ch << " ";
}
cout << endl;

但要注意,ch只是s2里面每个字符的拷贝,相当于是一个局部变量,在使用iterator一样的修改字符功能时,不会影响s2的原字符串。

auto不能空定义和定义数组。如auto e;这样,会导致编译器报错,还有auto不能做参数,但可以做返回值(谨慎使用,不太方便)

auto最大的作用就是用来简化代码;范围for适合用于数组和容器。

cpp 复制代码
int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
// C++98的遍历
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
    {
        array[i] *= 2;
    }
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
    {
        cout << array[i] << endl;
    }
// C++11的遍历
    for (auto& e : array)
        e *= 2;
    for (auto e : array)
        cout << e << " " << endl;
    
    string s1("hello world");
    //string::const_iterator cit = s3.begin();
    auto cit = s3.begin();

    const string s2("hello world");
    //string::const_iterator cit = s3.rbegin();
    auto rcit = s3.begin();
}

太多了就不一一写出来,具体功能可以去看看上面了成员函数了解和网站

typeid

typeid可以帮助我们看类型:

cpp 复制代码
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = func1();
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
}
capacity

capacity可以查看当前分配空间大小,当size增长到超过当前分配空间时(大于等于16),容器会自动重新分配更大的内存。

空间扩容机制:string开的前空间填满后,会自动扩容。直接看的话结论是第一次二倍扩容,第二次以后一点五倍扩容,当然不同编译器也会有区别,一般都认为是两倍扩容。

这里我的编译器出现二倍扩容是因为第一次扩容时有一个空间(容量固定为16)转换到另一个空间(可扩容空间,扩容为固定空间的两倍,接后续扩容以原1.5倍扩容)来特殊处理存储。或者当存入的size大于原有的空间,也会直接开一个新的空间存放。

capacity返回的数组是不包含\0的,返回的值是默认减1。如实际16的空间返回值是15。

cpp 复制代码
void TestPushBack()
{
    string s;
    size_t sz = s.capacity();
    cout << "making s grow:\n";
    for (int i = 0; i < 100; ++i)
    {
        s.push_back('c');
        if (sz != s.capacity())//capacity变化时打印新的容量
        {
            sz = s.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}
reserve()

频繁扩容要不断开空间,是一件不好的事情,所以妩媚可以提前预留空间以减少扩容次数。

预留、固定开多少空间,如reserve(100),是开辟100大小的空间,但实际上编译器开辟空间会做一个字节对齐,开辟的空间肯定会比要求开辟的空间大一些。

同样的,因为不计算\0,所以我们不能直接开辟对等大小的空间,要多大一点预留位置。

reserve可以提前开空间,提高效率,避免反复扩容。

reserve还可以发起一个不具有约束力的请求缩小容量。

reserve (size_t n = 0);

n大于capacity开辟的值当前空间会主动扩容。

n小于原有size的值,最多缩小到与size一样的空间大小。

n在capacity与size之间时,不确定了,不同编译器的做法不一样,我这的VS编译器是选着不缩保留已经开辟的空间 。

operator[a]与at(a)

读取第a个位置的字符,也有const的版本

at和operator[a]与at(a)[]功能一样,但越界是会抛出异常,poerator[]则会断言。

cpp 复制代码
string s = "hello";
char ch = s[1];
s[2] = 'X';

char ch = s.at(1); 
s.at(2) = 'X';

其余简单介绍:

stoi:字符串转整形,to_string将整形转化为字符串int。

to_string:其他类型转换成字符串

insert:在指定位置插入字符 / 字符串的成员函数。

swap:可以交换两个string对象的内容

cpp 复制代码
string s1 = "hello";
string s2 = "world";
s1.swap(s2); 
swap(s1, s2);
// 交换s1和s2的内容
// 交换后:s1="world",s2="hello"
相关推荐
郑泰科技1 天前
fmm(快速地图匹配)实践:Boost header not found解决方案
c++·windows·交通物流
郝学胜-神的一滴1 天前
Linux线程使用注意事项:骈文技术指南
linux·服务器·开发语言·数据结构·c++·程序人生
叫我:松哥1 天前
基于 Flask 的音乐推荐与可视化分析系统,包含用户、创作者、管理员三种角色,集成 ECharts 进行数据可视化,采用混合推荐算法
开发语言·python·信息可视化·flask·echarts·pandas·推荐算法
此剑之势丶愈斩愈烈1 天前
mybatis-plus乐观锁
开发语言·python·mybatis
CC.GG1 天前
【Qt】常用控件----容器类控件(QGroupBox、QTabWidget )以及布局管理器
开发语言·qt
星火开发设计1 天前
折半插入排序原理与C++实现详解
java·数据结构·c++·学习·算法·排序算法·知识
缘如风1 天前
Qt Creator 断点调试断点停不住
开发语言·qt
ghie90901 天前
MATLAB中实现基于高斯混合模型(GMM)的心电信号两级分类
开发语言·matlab·分类
勘察加熊人1 天前
python实现批量中英文文件翻译
开发语言·windows·python