目录
一、STL简介
1.1STL的版本
1.2STL六大组件
1.3STL的重要性及缺陷
二、string类简介
2.1string类了解
2.2为什么学习string类
三、string类使用(常用接口)
3.1string类的成员函数
3.1.1构造函数
3.1.2析构函数
3.1.3“=”运算符重载函数
3.2迭代器(iterator)+string类对象的访问及遍历
3.2.1 iterator
3.2.2 operator[]
3.2.3 at()
3.2.4 back() && front()
3.2.5 范围for
3.3string类对象的容量操作
3.3.1 size() && length()
3.3.2 capacity() && reserve && resize
3.3.3 empty() && clear()
3.4string类对象的修改操作
3.4.1 push_back && append && assign && insert
3.4.2 operator+=
3.4.3 c_str()
3.4.4 find+ npos && substr()
3.4.5 erase
一、STL简介
STL(standard template libaray-标准模板库):从表面意思来说,他是c++标准库的重要组成成分,他的组件(接口)是可复用的,管理着数据结构和算法。
1.1STL的版本
- 原始版本--HP版本(所有STL实现版本的始祖),开源版本,允许任何人使用,但后人如对其修改,也需开源。
- P.J.版本--继承自HP版本,在 HP 版本的基础上进行了大量的独立创作和投入,形成了新的商业价值,那么他可以根据自己的意愿选择是否收费,并制定自己的授权协议。被Windows Visual C++采用,不能公开或修改,那么必然有一定的缺陷不容易发现,可读性较低。
- RW版本--继承HP版本,被C++ Builder采用,同样不能修改,可读性较低
- SGI版本--继承HP版本,被GCC(Linux)采用,开源版本,可移植性好,阅读性非常高。在学习过程中,就可以参照这个版本的源码进行研究。
1.2STL六大组件
STL有六大组件,通过一张图来展示,要学的第一个组件就是容器,这些容器接口说白了就是管理数据结构,最重要的基础就是string接口,string产生其实比STL还要早,后来才归类进来,学会这一个,就可以贯通各个接口,因为用法都大差不差。
1.3STL的重要性及缺陷
STL在笔试中、面试中占了很重要的一部分,在工作中也会经常使用,在学习STL的过程中,也不能急于求成,先学会怎么用,再去深入了解底层,再扩展。当然在学习过程中也可能遇到以下问题,当然这应该也是STL的缺陷。
1.STL更新慢。
2.STL现在没有只差线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的
3.STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取
4.STL的使用会有代码膨胀的问题,当然这是模板语法本身导致的。
接下来我们进行学习第一个容器接口string类,并结合所学理解STL的缺陷。
二、string类简介
2.1string类了解
1.string类其实就是一个模板类,typedef过来的,是basic_string模板类的一个实例,用的char来实例化basic_string模板类。
2.管理字符序列的一个类
3.string类有许多接口,这些接口都是用来操作操作字符串
4.这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
2.2为什么学习string类
其实还是一个原则,string类可以更好的面向对象,string类的接口都是拿来使用就行,不需要过分管理底层如何实现,而C语言就不同了,轮子需要自己造,空间需要自己管理 ,不能出错。
那么接下来,就进行学习string类的常用接口有哪些,以及如何使用。
三、string类使用(常用接口)
在介绍string类接口前先说明一下接口会出现的npos,他是成员常量,在接口中扮演缺省值成分,size_t为unsigned int,无符号整形的-1,即整形最大值。也就是不传参,默认就是缺省值。其次在使用string类的接口时包含一下头文件#include <string>
static const size_t npos = -1;
3.1string类的成员函数
3.1.1构造函数
default (1) | string();
| |
---|---|---|
copy (2) | string (const string& str);
| |
substring (3) | string (const string& str, size_t pos, size_t len = npos);
| |
from c-string (4) | string (const char* s);
| |
from sequence (5) | string (const char* s, size_t n);
| |
fill (6) | string (size_t n, char c);
|
#include <iostream>
#include <string>
using namespace std;
int main()
{string str;//构造空对象,调用默认构造函数string s("hello world");//用c-string来构造string类对象string s1(5, 'x');//创建5个字符xstring s2(s1);//s1拷贝给s2string s3(s, 5, 3);//从第五个位置,拷贝3个字符给s3string s4(s, 2);//从第2个位置,npos为整形最大值,默认拷贝后面所有字符给s4,包括'/0'string s5("hello world", 5);//拷贝前5个字符给s5cout << s << endl;cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;cout << s5 << endl;return 0;
}
运行结果:
3.1.2析构函数
~string():这个基本上不用,编译器会自动调用析构函数,释放对象
3.1.3“=”运算符重载函数
string (1) | string& operator= (const string& str); |
---|---|
c-string (2) | string& operator= (const char* s); |
character (3) | string& operator= (char c); |
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");string s1 = s;string s2 = "hello world";//string s3 = 'x';这个不支持,这种构造形式只能是字符串,不能是单个字符string s3;s3 = 'x';cout << s << endl;cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;return 0;
}
运行结果:
3.2迭代器(iterator)+string类对象的访问及遍历
迭代器本质上可以是内置类型,如typedef过的指针(例如:typedef char* iterator; typedef const char* const_iterator),也可以是其他类型(自定义类型),虽然迭代器底层实现方式不同,但在表面上其功能是一样的,当作指针来理解使用。其次,迭代器可以适用所有容器,例如:string、vector、list、stack、queue等
3.2.1 iterator
迭代器的范围对于对象而言是左闭右开:[ ),在字符串当中代表最后一个'/0'位置,并不算是有效字符
迭代器是属于string的类域,所以调用迭代器时,要指明类域,即string::iterator
迭代器作用于的成员函数有8个: begin(),end(),rbegin(),rend(),cbegin(),cend(),crbegin(),crend()。
他作用于的成员函数有各自的重载函数。
①begin + end(作用:正向遍历字符串)
iterator begin():返回指向第一个字符串的迭代器
iterator end():返回指向最后一个字符串下一个位置的迭代器,可以认为指向'/0'
const_iterator begin() const:返回指向第一个字符串的迭代器,但迭代器所指向的内容不能修改
const_iterator end() const;返回指向最后一个字符串的迭代器,但迭代器所指向的内容不能修改
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");const string s1("excuse me");string::iterator it = s.begin();//指向字符串的第一个位置,返回对象为迭代器string::const_iterator it1 = s1.begin(); //返回指向第一个字符串的迭代器,但迭代器所指向的内容不能修改while (it != s.end())//遍历{cout << *it;//迭代器当作指针,进行解引用//*it = 'x';可以修改++it;}cout << endl;while (it1 != s1.end())//遍历{cout << *it1;//迭代器当作指针,进行解引用//*it = 'x';不能修改++it1;}cout << endl;return 0;
}
②rbegin + rend(作用:反向遍历字符串)
reverse_iterator rbegin():反向迭代器,其功能跟begin相反,返回指向最后一个字符的迭代器
reverse_iterator rend():其功能与end相反,返回指向第一个字符的迭代器
const_reverse_iterator begin() const:返回指向最后一个字符串的迭代器,但迭代器所指向的内容不能修改
const_reverse_iterator end() const:返回指向第一个字符的迭代器,但迭代器所指向的内容不能修改
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");const string s1("excuse me");string::reverse_iterator it = s.rbegin();//指向字符串的最后一个位置,返回对象为迭代器string::const_reverse_iterator it1 = s1.rbegin(); //返回指向最后一个字符串的迭代器,但迭代器所指向的内容不能修改while (it != s.rend())//反向遍历{cout << *it;//迭代器当作指针,进行解引用//*it = 'x';可修改++it;}cout << endl;while (it1 != s1.rend()){cout << *it1;//迭代器当作指针,进行解引用//*it1 = 'x';不能修改++it1;}return 0;
}
③cbegin + cend ④crbegin + crend
这两个组合就是说明调用的是const对象的迭代器,当调用的是const对象迭代器时,前缀有符号c和不加c都是一样的。
例如:const s1("hello world"); 定义一个const对象,调用const的迭代器。
string::const_iterator it = s1.cbegin();或者string::const_iterator it1 = s1.begin();
同理另外几个也是一样的,所以这两个组合也没啥特别大的作用。
3.2.2 operator[]
属于string类,运算符[ ]的重载函数,直接由string对象调用访问对象元素。
重载版本:
char& operator[](size_t pos):返回pos位置的字符,由const string类对象调用
const char& operator[](size_t pos) const:返回pos位置的字符,是一个常量引用 ,不能对其修改,且在函数内部不能修改对象的成员变量。
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");const string s1("excuse me");for (size_t i = 0; i < s.size(); i++){cout << s[i];}cout << endl;for (size_t i = 0; i < s1.size(); i++){cout << s1[i];//s[i] = 'x';不能修改常量引用}return 0;
}
3.2.3 at()
功能跟operatro[]一样,调用方式不同罢了
重载版本:
char& at(size_t pos);
const char& at(size_t pos) const;
using namespace std;
int main()
{string s("hello world");const string s1("excuse me");for (size_t i = 0; i < s.size(); i++){cout << s.at(i);}cout << endl;for (size_t i = 0; i < s1.size(); i++){cout << s1.at(i);//s[i] = 'x';不能修改常量引用}return 0;
}
3.2.4 back() && front()
分别返回最后一个字符和第一个字符
重载版本:
char& back(); const char& back();
char& front(); const char& front();
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");const string s1(s);cout << s.back() << endl;s.back() = '!';//修改最后一个字符cout << s.back() << endl;cout << s.front() << endl;s.front() = '!';//修改第一个字符cout << s.front() << endl;cout << s1.back() << endl;cout << s1.front() << endl;return 0;
}
3.2.5 范围for
他的底层其实就是用迭代器来实现的,调用的就是迭代器,只不过在表面上的用法不同而已
形式:
for(auto e : 对象) {}; 或者for(auto& e : 对象) {};
e对象的名称可以随便取,一般用e,auto在类和对像章节讲过,根据赋值对象的类型来推演被赋值对象的类型。如何使用范围for的形式,具体也要看对象中的成员赋值给e时,是否要调用拷贝构造,而造成没必要的浪费。
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");const string s1 = "string is easy to learn";for (auto e : s)//依次取s中的元素到e中,底层调用的就是迭代器。依次打印e中的内容{cout << e;}cout << endl;for (auto d : s1){cout << d;}
}
3.3string类对象的容量操作
3.3.1 size() && length()
函数原型:
size_t size() const; size_t length() const;
他们两的作用是一样的,都是获取字符串的长度,至于为什么有两个,这是因为历史上的原因,早期是先出来的length函数,但后来如果用length来表示树的大小就不合适,所以又造了个size函数,就是为了更符合表面意义,本质上没区别。
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");cout << s.size() << endl;cout << s.length() << endl;return 0;
}
3.3.2 capacity() && reserve && resize
函数原型:
size_t capacity() const;返回string对象容量的大小。
void reserve(size_t n);对容量进行操作,vs下只能扩容,不能缩容
void resize(size_t); void resize(size_t, char c);对_size 和容量和数据的操作,vs下对容量的操作只能扩容
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");cout << s.size() << endl;cout << s.capacity() << endl;//返回空间总大小,可能比字符串的长度大,可能采用了对齐规则s.reserve(30);//扩容cout << s.capacity() << endl;//n是相对字符串的大小//n<resize<capacity,进行尾插数据,若未指定尾插数据默认为0s.reserve(20);//vs下reserve不支持缩容,所以还是和前面的容量一样s.resize(16, 'y');//剩余的空间都尾插'y'cout << s.capacity() << endl;cout << s << endl;//n>capacity,进行扩容,然后尾插数据s.resize(46, 'x');//扩容后多余的空间都尾插'x'cout << s.capacity() << endl;cout << s << endl;//resize<n,删除数据,只取前面resize个数据,此时resize不会缩容,但size大小变了s.reserve(30);s.resize(5);cout << s.capacity() << endl;cout << s << endl;return 0;
}
3.3.3 empty() && clear()
函数原型:
bool empty() const;判断字符串是否为空
void char();清空字符串中的内容,变为空串
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("hello world");cout << s << endl;cout << s.empty() << endl;//s不为空while (!s.empty())//判断s对象是否为空,不是就清空{s.clear();}cout << s.empty() << endl;//s为空s = 'w';//插入一个字符cout << s << endl;return 0;
}
3.4string类对象的修改操作
3.4.1 push_back && append && assign && insert
函数原型:
void push_back(char c);尾插一个字符
append(部分重载):
string& append (const string& str);追加字符串的拷贝
string& append (const char* s);追加字符串的拷贝
string& append (const char* s, size_t n);追加字符串的前n个字符
string& append (size_t n, char c);追加n个字符c
assign的原型跟append的一样,但它的功能是赋值,而不是追加
insert(部分重载):
string& insert (size_t pos, const string& str); pos位置插入字符串
string& insert (size_t pos, const char* s); pos位置插入字符串
string& insert (size_t pos, const char* s, size_t n);pos位置插入字符串的前n个字符
string& insert (size_t pos, size_t n, char c);pos位置插入n个字符c
void insert (iterator p, size_t n, char c);迭代器位置插入n个字符c
iterator insert (iterator p, char c);迭代器位置插入字符c
#include <iostream>
#include <string>
using namespace std;
int main()
{string s;s.push_back('h');//尾插字符s.push_back('e');cout << s << endl;s.append("llo");//尾插字符串s.append("world", 5);cout << s << endl;s.assign("excuse me");//赋值cout << s << endl;string s2;string s3 = "world";s2.insert(0, "hello");//在0位置插入字符串cout << s2 << endl;s2.insert(s3.size(), s3);cout << s2 << endl;s3.insert(0, 1, 'h');//0位置插入1个字符hs3.insert(s3.begin() + 1, 1, 'e');//迭代器+1位置插入1个字符es3.insert(s3.begin() + 2, 2, 'l');//迭代器+2位置插入2个字符ls3.insert(s3.begin() + 4, 'o');//迭代器+4位置插入字符ocout << s3 << endl;return 0;
}
3.4.2 operator+=
函数原型(重载):
string& operator+= (const string& str);尾插字符串
string& operator+= (const char* s);尾插字符串
string& operator+= (char c);尾插字符
#include <iostream>
#include <string>
using namespace std;
int main()
{string s;string s1 = "hello";s += s1;//尾插字符串s += ' ';//尾插字符空格s += "world";cout << s << endl;return 0;
}
3.4.3 c_str()
函数原型:
const char* c_str() const;返回指向数组的指针,该数组包含以空结尾的字符序列(即C字符串),数组中的值跟对象中的值一样。就是获取字符串,以c字符形式返回
#include <iostream>
#include <string>
using namespace std;
int main()
{string s1 = "hello";//const char* str=s1;×const char* str = s1.c_str();//✓cout << str << endl;return 0;
}
3.4.4 find+ npos && substr()
函数原型:
find:
size_t find (const string& str, size_t pos = 0) const;在pos位置查询字符串,默认从0开始
size_t find (const char* s, size_t pos = 0) const;在pos位置查询字符串,默认从0开始
size_t find (const char* s, size_t pos, size_t n) const;在pos位置查询字符串的前n个字符
size_t find (char c, size_t pos = 0) const;在pos位置查询字符,默认从0开始
若查询到字符串,则返回字符串的起始位置的下标,否则返回npos;
substr:
string substr (size_t pos = 0, size_t len = npos) const;pos位置往后取len个字符返回给新对象,默认从0位置开始取。
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("test1.cpp hello world");string s1("https://leetcode.cn/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/description/");size_t pos1 = s1.find(':');//默认从0位置查询字符cout << s1.substr(0, pos1) << endl;;size_t pos2 = s1.find("/",pos1+3);//从Pos+3的位置查询字符串cout << s1.substr(pos1 + 3, pos2-pos1-3) << endl;//从POS+1位置取POS-POS1+3个字符cout << s1.substr(pos2 + 1)<<endl;//默认从0开始取POS2+1个字符cout << s.c_str() << endl;cout << s.substr(2)<<endl;size_t pos3 = s1.find("/");while (pos3 != string::npos){s1.replace(pos3, 1, "*");//pos3位置,替换一个字符pos3 = s1.find("/", pos3 + 1);}cout << s1 << endl;return 0;
}
运行结果:
3.4.5 erase
string& erase (size_t pos = 0, size_t len = npos);从Pos位置删除len个字符,默认从0开始,默认删除所有字符
iterator erase (iterator p);删除迭代器位置的字符
iterator erase (iterator first, iterator last);删除迭代器区间的字符
#include <iostream>
#include <string>
using namespace std;
int main()
{string s("test1.cpp hello world");cout << s << endl;s.erase(0,5);//从0位置删除5个字符cout << s << endl;s.erase(0, 4);//从0位置删除4个字符cout << s << endl;s.erase(s.begin());//删除迭代器位置cout << s << endl;s.erase(s.begin(), s.begin() + 2);//删除迭代器区间的字符cout << s << endl;s.erase();cout << s << endl;//s为空return 0;
}
end~