个人主页 : zxctscl 如有转载请先通知
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可 能还会越界访问。
string的学习离不开“看”,这里推荐两个网站:一个是:https://legacy.cplusplus.com/:
还有一个C++文档的官网是: https://en.cppreference.com/w/:
更喜欢第一个网站,这里面还有c的库。在之后的博客中也是以第一个网站。
在第一个网站里面直接搜索就会看到:
它是一个字符顺序表:
它底层也是模板是basic_string:
总结: 1、string是表示字符串的字符串类 2、 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。 3、string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string; 4、不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
(constructor)函数名称 | 功能说明 |
---|---|
string()(重点) | 构造空的string类对象,即空字符串 |
string(const char* s)(重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符 |
string(const string&s)(重点) | 拷贝构造函数 |
查看string类的Member functions
:
点constructor就会看见7个构造函数:
其他的构造函数看说明就很简单了解,但是来看看substring (3) ,它当中的size_t len = npos
是什么意思呢?
如果len这个参数给的超过了这个字符串从pos位置开始给的长度,举个例子:从pos位置开始,剩余的长度是10,那么它超过了10,有多少就给多少,直接取到结尾。 如果len超过了剩余的长度,或者给npos缺省参数,那么这个就是从pos位置开始,直接取到结尾,有多少就取多少。
点开来看看npos: npos是string里面的一个静态成员常量。
为什么给的是负一就取到字符的结尾?size_t
无符号整形这里虽然存的是-1,但底层存的是补码,无符号整形原码和补码是一样的,这里反而变成了整形的最大值,也就是2^32-1。
来实现一下:
#include<iostream>
using namespace std;
#include<string>
void test_string1()
{
string s0;
string s1("hello world");
string s2(s1);
string s3(s1, 5, 3);
string s4(s1, 5, 10);
string s5(s1, 5);
cout << s0 << endl;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
cout << s5 << endl;
}
int main()
{
test_string1();
return 0;
}
来看看s3:从第5个位置开始,那就是从空格开始,拷贝3个字符就是到o的位置。
2. string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间** |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
注意:
那么要用什么样方式遍历呢?
void test_string2()
{
string s1("hello world");
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
}
string是私有的,不能拿到它的下标,想要获取它的长度,就用size
,返回它有多少个字符。
std::string::operator[]
这里模拟的是数组。
a[i]就相当于*a(a+i),就是返回它的第i个字符。
它的底层简单来实现一下: 获取pos位置那个字符
class string
{
public:
char& operator[](size_t pos)
{
return _str[pos];
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
来试试修改pos位置的字符:
void test_string2()
{
string s1("hello world");
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < s1.size(); i++)
{
s1[i] ++;
}
cout << endl;
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
}
如果给的是const对象就不能修改。
iterator是一个类型定义在string里面,所以它要指定类域,才能取到。
string::iterator it3 = s3.begin();
while (it3 !=s3.end())
{
cout << *it3 << " ";
++it3;
}
cout << endl;
迭代器行为像指针一样的类型对象,这个区间是一个左闭右开。
迭代器像指针,但不是指针,可以来一下它的类型。
用迭代器也可以实现string的修改:
it3 = s3.begin();
while (it3 != s3.end())
{
*it3-=3 ;
++it3;
}
cout << endl;
这两种方式其实没有什么区别,那为什么会有迭代器呢? 迭代器是主流,它屏蔽了底层的细节,它才是容器的核心访问方式。链表不会用下标访问。 来看个例子:
#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<list>
void test_string2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator itt = lt.begin();
while (itt != lt.end())
{
cout << *itt << " ";
++itt;
}
cout << endl;
}
范围for 自动取容器中的数据,赋值给e,自动迭代,自动往后走,自动结束。
for (auto e : s3)
{
cout << e << " ";
}
cout << endl;
范围for底层就是迭代器。
有问题请指出,大家一起进步!!!