?????????????????????????????????????
1、什么是STL
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。
1.1、STL的六大组成
2、string
2.1、string文档介绍
默认成员函数
迭代器
容量相关的
C语言中:string
串
字符数组,可以扩容,可以增删改查。(更符合的信息都是字符串存的,比如身份证,名字,地址...)
数据和方法分离(空间和使用)
C++中:string
2.2、string的构造
提供了7个
2.3、string类对象的常见构造
就前3个常用,其余的不常用。
带参构造、无参构造、拷贝构造
关于npos
size_t表示无符号整形,所以npos为FFFF FFFFH
2.4、string类对象的容量操作
知识点
operator[]
底层:
一个指针,一个大小,一个容量(用来扩容)。
2.5、我们如何遍历自定义类型的字符串呢?
方法1:size( ) + operator[ ]
这里自定义类型s1可以像数组一样访问,而且访问越界有报错。
void test_string3(){string s1("hello world");//计算s1大小cout << s1.size() << endl;//访问字符串// 方法1:operator[]//自定义类型s1,可以像数组一样访问//可读,可写,可修改//可读for (size_t i = 0; i < s1.size(); i++){//写全//cout << s1.operator[i] << " ";cout << s1[i] << " ";}cout << endl;s1[0] = 'x';cout << endl;//可修改for (size_t i = 0; i < s1.size(); i++){s1[i]++;}}
放法2:迭代器
先看代码???
void test_string4(){string s1("hello world");//iterator定义在类域里,只能在类域中搜索。string::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " ";++it1;}cout << endl;}
代码中出现begin( ) 和 end( )
如何理解呢?
解析:
我们可以把begin和end看作指针,begin指第一个字符,end指最后一个字符的下一个位置。结合代码,begin把第一个字符的地址给给it1,然后解引用it1取到值,++it1走到下一个位置,知道遇到end()就停止。
注意:
\0是表示字符,不是有效字符
[ begin,end ) 为左闭右开区间,
对比方法一和方法二:
方法一只适合下标访问,空间必须是连续的,所以只适用于string;
方法二迭代器是主流的访问方式,
举例链表使用迭代器访问
总结:
begin( ):任何容器返回第一个数据位置的iterator
end( ):任何容器返回最后数据的下一个位置的iterator
方法3:范围for(所有容器都支持)
for (auto e : s1){cout << e << " ";}cout << endl;
自动取s1里的值,赋值给e,自动++,自动判断结束。
底层角度,就是迭代器
注意:
这里是*it赋值给给e,赋值拷贝,并不会影响数据。
2.6迭代器iterator
普通迭代器和const迭代器
我们先看一下const迭代器
如图,这里是一个const string,就不能用之前那种方式遍历了,
因为const string 是只读的,这里我们调用
修改代码:???
总结:
iterator 可读可写
const_iterator 只读
普通的迭代是给普通的string用的
const的是给const对象用的,保护string不能修改数据,可以遍历
这里我们再深究一下:
为什么const_iterator这洋写,而不是const iterator
因为const iterator 保证的是迭代器本身不能写
而const_operator 保证的是迭代器指向的数据不能写
反向迭代器
rbegin 和 rend
均只读
代码:???
string s2("hello world");string::reverse_iterator it2 = s2.rbegin();while (it2 != s2.rend()){cout << *it2 << " ";++it2;}cout << endl;
底层重载operator++来实现自定义类型的++。
迭代器总结
共有4中迭代器
iterator const_iterator reverse_iterator const_reverse_iterator
差异就在读和写部分
iterator 可读可写
const_iterator只读
reverse_iterator 只读
const_reverse_iterator 只读
2.7string插入字符
push 和 append
插入字符
但这两种方式都不常用
还是喜欢用重载运算符
assign
assign:赋值字符串,但赋值通常用operator=,运算符重载。
这里我们浅浅了解一下,熟悉一下看文档。
insert、erase和repalce
insert插入
string的insert慎用,
因为时间复杂度很大,效率低。
库中没有给出头插一个字符怎某写,
这里我们这样:???
char ch = 'y';cin >> ch;s2.insert(0, 1, ch);//头插一个cout << s2 << endl;
insert的使用:???
void test_string8(){string s1("hello world");cout << s1 << endl;s1.assign("11111");cout << s1 << endl;string s2("hello world");s2.insert(0, "xxxx");cout << s2 << endl;char ch = 'y';cin >> ch;s2.insert(0, 1, ch);//头插一个cout << s2 << endl;s2.insert(s2.begin(), 'y');cout << s2 << endl;s2.insert(s2.begin(), s1.begin(), s1.end());//把s1的头到尾,插入s2cout << s2 << endl;}
erase和replace
erase:
慎用,效率低。
replace:
把我的一部分替换
erase的使用???
void test_string9(){string s1("hello world");cout << s1 << endl;s1.erase(0, 1);//去掉第一个字符cout << s1 << endl;s1.erase(5, 100);//去掉5 - 100,后边没有也不报错,cout << s1 << endl;//replace效率不高,慎用,和insert类似,需要挪动数据string s2("hello world");s2.replace(5, 1, "%20");cout << s2 << endl;}
说到替换,那把所有空格都替换怎某实现呢?
代码???
//把空格全部替换成%20//方法一:循环替换,但时间复杂度大string s3("hello world hello bit");for (size_t i = 0; i < s3.size();){if (s3[i] == ' '){s3.replace(i, 1, "%20");i += 3;//跳过%20这三个字符}else{i++;}}cout << s3 << endl;//方法二:以空间换时间,string s4("hello world hello bit");string s5;for (auto ch : s4){if (ch != ' '){s5 += ch;}else{s5 += "%20";}}cout << s5 << endl;
这里提供了俩方法:
1.循环替换
2.另辟蹊径
我们这里不推荐方法一循环替换,为什么呢?
因为时间复杂度非常大,方法二以时间换空间。
看到这里,我们小试牛刀,练练手。
小试牛刀
.仅仅反转字母(双指针)
.字符串中的第一个唯一一个字符(映射)
.验证回文串(回文:对称),题目:只判断字母
max_size()
字符串的最大长度(用的不多),因为类型开不出这样大的空间
reserve和resize
reserve改变capacity
resize改变size,也可以改变capacity
reserve:扩容(提前开好空间,不适用于缩容)
在vs中不能缩容(看编译器)
string s1("11111111111");string s2("1111111111111111111111111111111");cout << s1.capacity() << endl;s1.reserve(100);cout << s1.capacity() << endl;s1.reserve(20);cout << s1.capacity() << endl;
reserve的使用
void TesePushBack(){string s;s.reserve(200);s[100] = 'x';}
开了空间是不能直接赋值的,因为[ ]在底层调用operator [ ],会调用size,而reverse只改变capacity,所以这时还需要初始化size
resize的使用
void test_string11(){string s1;s1.resize(5);s1[4] = '3';s1[3] = '4';s1[2] = '5';s1[1] = '6';s1[0] = '7';}
resize不会填充前面已有数据,会在后面补
string s2("hello world");s2.resize(20, 'x');
at
越界,异常可以捕获。
string s2("hello world");//s2[30];//s2.at(30);try{s2.at(30);}catch (const exception& e){cout << e.what() << endl;}
s2[30] 直接报错,
s2.at(30)会捕获异常
operator是暴力型的,at是温柔型的。
c_str()
文件读取
#define _CRT_SECURE_NO_WARNINGS 1string file("Test.cpp");FILE* fout = fopen(file.c_str(), "r");//获取底层指向char的那个指针。char ch = fgetc(fout);while (ch != EOF){cout << ch;ch = fgetc(fout);}
find( )、rfind( )和substr( )
find()
查找字符
这里掌握第一个和第四个
第一个:查找一个striing,pos位置
第四个:从pos位开始找,一个字符
find() : 正在查找
rfind():倒着查找
分开网址
取https
取gitee.com
find : 从pos1 + 3位置开始找 ‘ / ’.
substr : 从pos + 3开始取,取pos2 -(pos1+3)这么长.
size_t pos2 = url.find('/', pos1 + 3);//从pos1 + 3开始找‘/’string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));//左开右闭,从pos + 3开始取,取pos2 -(pos1+3)这么长cout << url2 << endl;
取剩下的
find_first_of 、 find_last_of
find_first_of
.把aeiou 全部变为 *
.相当于c中的strtok( ),
用的非常少
void test_string13(){std::string str("Please, replace the vowels int this sentence by asterisk.");std::size_t found = str.find_first_of("aeiou");while (found != std::string::npos){str[found] = '*';found = str.find_first_of("aeiou", found + 1);}std::cout << str << '\n';}
find_last_of
从后忘前找
operator +
.提供了
string + string
string + char*
char* + string
string + char
//operator + 写成了全局函数,为了保证ret3能够实现void test_string14(){string s1 = "hello";string s2 = "world";string ret1 = s1 + s2;//string + stringcout << ret1 << endl;string ret2 = s1 + "xxxxxx";//string + char*cout << ret2 << endl;string ret3 ="xxxxxx" + s1;//char* + stringcout << ret3 << endl;//字典序比较,比较ascii码值cout << (s1 < s2) << endl;}
getline()
先看一题:字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)
原因在于cin和scanf在遇到空格后就结束了(流提取问题),
规定空格或换行是多个值之间的分割
所以这里用getline(cin,str),获取一行中包含空格,不能用 >>
。
。
这里我们看一个代码:持续获得字符串
string str;//默认规定遇到空格或换行是多个值之间的分割while (cin >> str){cout << str << endl;}
当输入换行时就停止了,那程序如何停止呢?
ctrl + c ,直接杀进程
ctrl + z + 换行
to_string
字符串转整形
c中 : stoi字符串转整型 和 itos整型转字符串
c++ : to_string
转整型,浮点型...
int main(){int x = 3, y = 0;cin >> x >> y;string str = to_string(x+y);//转整型cout << str << endl;}
int z = stoi(str);//字符串转整型
3.vs和g++下string结构的说明
3.1vs下
注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字。
符串的存储空间:
当字符串长度小于16时,使用内部固定的字符数组来存放
当字符串长度大于等于16时,从堆上开辟空间
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
3.2g++下
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,
内部包含了如下字段:
·空间总大小
·字符串有效长度
·引用计数
指向堆空间的指针,用来存储字符串。
?????????????????????????????????????
last but not least,创作不易,望读者三连三连三连支持?
重要的事情说三遍?