C++ 带你吃透string容器的使用(上)

简介: C++ 带你吃透string容器的使用

接下来我们就要正式进入C++中STL的学习当中了

今天我们介绍的是STL中的string容器

我们对于STL的学习可以借助文档去学习:

我们今天就要通过cplusplus这个网站来介绍string容器

一.string容器概述

首先我们要了解的是:

什么是string容器?

注意:使用string容器需要包含头文件:

在了解了string容器之后,我们先来学习一下string容器的使用

二.string容器的使用

1.string容器的默认成员函数

我们先来介绍string容器的默认成员函数,这里只显示了构造函数(其中也包含拷贝构造函数),析构函数,赋值运算符重载这4个默认成员函数.

至于剩下的那两个取地址运算符重载函数则不是很重要,我们平常也不需要特别关心那两个默认成员函数

那么下面我们就去介绍这4个默认成员函数

1.构造函数和拷贝构造函数

下面我们一一演示,并说明用法

第一种和第二种:

//这是最常用,最常见的两种用法
string s1;//第一种用法:无参构造
string s2("hello string");//第二种用法:含参构造,使用常量字符串来初始化s2
string s3 = s2;//注意:拷贝构造,而不是赋值运算符重载!
s3 = s1;//这个才是赋值运算符重载
string s4(s3);//拷贝构造

下面我们来看第三种用法:

string s2("hello string");
string s4(s2, 1, 6);//ello s
string s5(s2, 0, 100);//hello string

下面来看第4种用法

其实这个重载版本没什么太大的价值

因为string容器的构造函数支持单参数的构造函数的隐式类型转换

C++类和对象下(初始化列表,静态成员,explicit关键字,友元)

关于单参数的构造函数的隐式类型转换的问题,大家可以看我的这篇博客,在介绍explicit关键字的时候有详细的介绍

const char* ptr = "hello C string";
string s6(ptr

下面来看第5种用法

const char* ptr = "hello C string";
string s7(ptr, 4);

下面来看第6种

至于第7种:

这个是跟迭代器有关的,我们目前先不谈,因为我们还没有介绍迭代器呢

在文章的最后的时候,我们就会揭开string容器的迭代器的神秘面纱

2.赋值运算符重载

string s9("hello operator=");
s9 = "hello world";
const char* s = "hello wzs";
s9 = s;
char ch = 'w';
s9 = ch;

3.析构函数

2.string容器的遍历和访问元素

这里要首先介绍两个函数

1.operator[]运算符重载

也就是说我们可以像访问数组一样来访问string容器

string s1 = "hello string";
for (int i = 0; i < s1.size(); i++)
{
  cout << s1[i] << " ";
}
cout << endl;
cout << s1 << endl;

同理因为s1没有被const修饰,所以调用的是operator[]的第一种重载版本

char& operator[] (size_t pos);
• 1

因此也可以进行修改

string s1 = "hello string";
for (int i = 0; i < s1.size(); i++)
{
  s1[i] ++;
  cout << s1[i] << " ";
}
cout << endl;
cout << s1 << endl;

介绍完operator[]的用法之后

让回到这个问题:为什么要重载两种版本呢?

因为会有const string这种类型的需求

重载了两个版本之后:这样就可以保证s1这个被const修饰的字符串不被[]所修改了

2.iterator迭代器

1.begin()和end()

首先我们要先介绍两个特殊的迭代器:begin()和end()

在这个位置处,我们可以暂时把iterator迭代器当做指针去使用,因此我们就可以这样去遍历访问元素了

string s2 = "hello iterator";
string::iterator it = s2.begin();
while (it != s2.end())//注意:我们使用iterator访问和遍历时要注意左闭右开使用[begin,end)
{
  cout << *it << " ";//这里可以暂时理解为像是指针解引用的用法一样
  it++;//这里可以暂时理解为像是指针自增(也就是后移)的用法一样
}
cout << endl;
cout << s2 << endl;

同样的,这个迭代器也可以用来改变这个string具体位置的元素的值

string s2 = "hello iterator";
string::iterator it = s2.begin();
while (it != s2.end())//注意:我们使用iterator访问和遍历时要注意左闭右开使用[begin,end)
{
  *it += 1;//(*it)++;这样也可以,不过不要忘了加小括号(运算符优先级的问题)
  cout << *it << " ";//这里可以暂时理解为像是指针解引用的用法一样
  it++;//这里可以暂时理解为像是指针自增(也就是后移)的用法一样
}
cout << endl;
cout << s2 << endl;

const string s2 = "hello iterator";
string::const_iterator it = s2.begin();
while (it != s2.end())//注意:我们使用iterator访问和遍历时要注意左闭右开使用[begin,end)
{
  //*it += 1;
  (*it)++;//err
  cout << *it << " ";//这里可以暂时理解为像是指针解引用的用法一样
  it++;//这里可以暂时理解为像是指针自增(也就是后移)的用法一样
}
cout << endl;
cout << s2 << endl;

2.rbegin()和rend()

迭代器也可以倒着遍历,就像这样:

可能这个英文解释不是很好理解,下面我们来演示一下:

//反向迭代器:rbegin(),rend()
string::reverse_iterator it = s1.rbegin();
while (it != s1.rend())
{
  cout << *it << " ";
  it++;
}
cout << endl;

同理,这个rbegin()和rend()也分为const迭代器和非const迭代器,

在这里就不赘述了,因为刚才讲begin()和end()的时候说明过区别

不过实际当中这个rbegin()和rend()并不常用

远远没有begin()和end()的出场率高

下面大家可能有疑问,这个迭代器访问元素哪有我直接下标访问香啊

这个迭代器也不过如此嘛

下面我们来说一下iterator的真正价值

3.iterator的真正价值

除此之外,借助迭代器还可以使用很多库函数的功能

比如:使用reverse逆置string,vector,list等等

#include <list>
#include <vector>
int main()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  vector<int>::iterator vit = v.begin();
  while (vit != v.end())
  {
    cout << *vit << " ";
    vit++;
  }
  cout << endl;
  list<double> lt;
  lt.push_back(1.1);
  lt.push_back(1.2);
  lt.push_back(1.3);
  lt.push_back(1.4);
  lt.push_back(1.5);
  list<double>::iterator lit = lt.begin();
  while (lit != lt.end())
  {
    cout << *lit << " ";
    lit++;
  }
  cout << endl;
  //对string逆置
  string s1("hello string");
  cout << s1 << endl;
  reverse(s1.begin(), s1.end());//依旧是给左闭右开:begin和end
  cout << s1 << endl;
  reverse(v.begin(), v.end());//这个reverse算法也可以适用vector,list
  vit = v.begin();
  while (vit != v.end())
  {
    cout << *vit << " ";
    vit++;
  }
  cout << endl;
  reverse(lt.begin(), lt.end());
  lit = lt.begin();
  while (lit != lt.end())
  {
    cout << *lit << " ";
    lit++;
  }
  cout << endl;
  return 0;
}

4.范围for

string容器也支持范围for的用法

关于范围for的知识,请看这篇博客:C++入门3+类和对象上

for (auto& e : s1)
{
  cout << e << " ";
}
cout << endl;

5.at()

关于at(),它跟[]的用法很像

string s1("hello world");
for (int i = 0; i < s1.size(); i++)
{
  cout << s1.at(i) << " ";
}

但是它们之间也存在一些差异

下面我们来演示一下:

这是[]来越界访问

这是at来越界访问

3.string容器与容量相关的函数

1.capacity,size,length

size()和length()我们前面提到过了,这里就不赘述了

接下来是容量capacity这个概念

2.reserve

下面我们来演示一下,顺便看一下在VS2019中的扩容机制

string s1;
int old_capacity = s1.capacity();
cout << old_capacity << endl;
for (int i = 0; i < 100; i++)
{
  s1.push_back('w');//将'w'这个字符尾插进入s1当中
  if (old_capacity != s1.capacity())
  {
    cout << s1.capacity() << endl;
    old_capacity = s1.capacity();
  }
}

string s1;
s1.reserve(100);
int old_capacity = s1.capacity();
cout << old_capacity << endl;
for (int i = 0; i < 100; i++)
{
  s1.push_back('w');//将'w'这个字符尾插进入s1当中
  if (old_capacity != s1.capacity())
  {
    cout << s1.capacity() << endl;
    old_capacity = s1.capacity();
  }
}

然后我们将插入的数据改为1000

看一下扩容的区别

string s1;
int old_capacity = s1.capacity();
cout << old_capacity << endl;
for (int i = 0; i < 1000; i++)
{
  s1.push_back('w');//将'w'这个字符尾插进入s1当中
  if (old_capacity != s1.capacity())
  {
    cout << s1.capacity() << endl;
    old_capacity = s1.capacity();
  }
}

扩容的机制在不同编译器下是不一样的

下面我们以Linux环境下的g++编译器来演示一下

一模一样的代码,我们来看一下运行结果

大家需要了解reserve的常见使用场景:提前扩容

3.resize

下面我们来演示一下:

第一种情况:

n

string s1("hello world");
//1.n<size
s1.resize(4);
cout << s1;

第二种情况:

size

string s1("hello world");
//2.size<n<capacity
cout << s1.capacity() << endl;
s1.resize(13);
cout << s1;

可见,的确是用’\0’来填充的

刚才我们看到:s1的容量是15

第三种情况:

n>capacity: 此时相当于先扩容,然后尾插字符c或者’\0’

下面我们来介绍一下resize的常见使用场景:

4.clear,empty

这两个函数都很简单,大家了解即可

4.尾插操作

下面这几个尾插操作都是自动扩容的,不需要我们操心

1.push_back

2.append

关于其他的用法,平常并不常用,大家知道即可

比如使用迭代器来append一段区间

3.+=运算符重载

真正常用的是这个+=运算符重载

相关文章
|
2月前
|
C++ 容器
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
C++ 容器
|
2月前
|
C++ 容器
|
2月前
|
存储 C++ 容器
|
2月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
50 0
|
2月前
|
存储 编译器 程序员
深度剖析C++string(上篇)(2)
深度剖析C++string(上篇)(2)
36 0
|
2月前
|
存储 Linux C语言
深度剖析C++string(上篇)(1)
深度剖析C++string(上篇)(1)
32 0
|
2月前
|
C++
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
20 0