string类
- 为什么学习string类
- 什么是string
- 标准库中的string类
- sting类的常用接口
- 构造接口
- string类对象的容量操作接口
- string类对象的访问及遍历操作接口
- string类对象的修改操作
- string类非成员函数
- vs和g++下string结构的说明(了解即可)
- vs下stirng的结构
- g++下string的结构
为什么学习string类
在c语言中,字符串是以’\0’结尾的一些字符集合,为了操作方便,c语言提供了一些str系列的库函数,但是这些库函数是于字符串分离开的,不太符合oop思想,而且底层需要用户自己管理,稍不留神可能还会越界访问
什么是string
string 就是字符串的意思,是 C++ 用来代替 char 数组 的数据结构。里面封装了一些常用的方法,方便我们对其进行一些操作,而且string的空间大小是动态变化的,大大减小了不必要的花销
标准库中的string类
string底层本质上还是一个顺序表,我们可以理解为是一个封装了char*的容器。
只不过:
-
char*
是一个指针 -
string
本质上是一个类,类的内部封装了char*
,即**string
是一个char*
**型的容器 -
**
string
管理char*
**所分配的内存,不用担心复制越界和取值越界等
typedef basic_string<char> string;
在使用string这个容器时,需要包含头文件
sting类的常用接口
构造接口
- 无参构造
- 字符串构造
- n个字符c构造
- 拷贝构造
string s1;//无参构造
**************************
string s1("hello world");//字符串构造
stirng s2(s1);//拷贝构造
**************************
string s1(10,'c');//n个字符构造
- 下面我会贴出运行图以便更好的理解:
string类对象的容量操作接口
- size();
- lenth();->与size()方法底层实现原理完全相同
- capacity()
- empty()
- clear()
- reserve()
- resize()
- 下面我会对这些接口进行演示
① size(),lenth(),capacity()
string s2("hello world");
cout << s2.size() << endl;
cout << s2.length() << endl;
size()
、lenth()
返回的是字符串有效字符个数,而capacity()
返回的是当前对象空间大小
- 为什么
size()
、lenth()
这两个接口的功能是一样的呢?这是因为某些历史原因遗留下来的,原本string类使用的是lenth()
接口,但是后面为了与其他STL容器保持统一,便有了size()
接口,那c++为什么不删了lenth()
这个接口呢?很简单,大白话讲就是:在size()接口出来前,很多项目使用的是lenth这个接口,如果删了这个接口,那些项目不就得全崩了吗?所以c++每次更新都是“向前兼容”,不会把之前不好的设定删除掉。
②empty()
- 这个接口的作用是判断该对象是否为空,为空在返回真,否则返回假。
- 下面是接口演示
③clear()
- 这个接口的作用是清空存储的字符串。
->注意,是把字符清楚,不会影响到空间,也就是capacity()。
- 下面是接口演示
- 它只是清空字符串,不会影响到空间
- 咱们来看下是不是真的清空了字符串
④reserve()
- 调整空间,若大于原空间,则进行扩容,若小于原空间,则不进行操作
- 下面是接口演示
一 . 当大于原空间时
在vs平台下,string类初始空间是15(平台不同开辟的空间也不同)
二 . 当小于原空间时
⑤resize()
- 调整字符串的长度,这个接口不仅会影响到size(),还会影响到capacity()
- 若大于原长度,但小于原空间,则会增加有效字符个数,直到达到给定的长度,多出来的部分则是用’\0’填充
- 若大于原长度,并且大于原空间,则会增加有效字符个数,直到达到给定的长度,并进行扩容
- 若小于原长度,则会减少有效字符个数,并且减去的部分可以理解为被删除了。
第一种情况
第二种情况
第三中情况
string类对象的访问及遍历操作接口
- operator[]—>返回pos位置字符的引用
- begin()+end()—>迭代器
- rbegin()+rend()—>反向迭代器
- 范围for—> C++11支持更简洁的范围for的新遍历方式
前面讲到string的底层本质是一个顺序表,既然是顺序表,那就能用下标引用操作符来访问与遍历。
① operator[] ->下标引用操作符重载
- 既然我们能通过下标引用来遍历string类的对象,那我们是不是也能借此来修改string类对象中存储的值呢?
答案是“肯定的”
- 另外,我们还能单独的对某一位置进行自增操作
②begin()+end()迭代器
begin()获取第一个字符的迭代器 + end()获取最后一个字符下一个位置的迭代器
- 下面是接口演示
什么是迭代器
概念:迭代器是一种检查容器内元素并遍历元素的数据类型,通常用于对C++中各种容器内元素的访问,但不同的容器有不同的迭代器,初学者可以将迭代器理解为指针。但记住,它只是类似于指针,但不是指针。
接下来,我就来具体的讲讲begin()
与end()
- bengin(),返回指向容器第一个元素位置的迭代器
- end(),返回容器最后一个元素的下一个位置的迭代器
- 现在来分析下前面的那段代码
string s1("hello world");//string::iterator是迭代器类型,不同的容器,迭代器类型不同string::iterator it1 = s1.begin();//it1存储s1.begin()返回的迭代器位置while (it1!=s1.end())//当it1没到s1.end()位置时{//如前面所说,我们可以暂且把迭代器当作指针来理解。在这里输出迭代器位置的值时,用到了"*"//和指针不同的是,这里的解引用操作符是被重载过的cout << *it1 << " ";//输出迭代器当前位置的值++it1;//然后迭代器后移}
③ rbegin()+rend()反向迭代器
rbegin()返回指向容器最后一个元素的迭代器,rend()返回指向容器第一个元素的前一个位置的迭代器。
- 接口演示以及代码讲解
string s1("hello world");
//string::reverse_iterator是反向迭代器的类型
string::reverse_iterator it1 = s1.rbegin();//it1存储s1.rbegin()的位置
while (it1!=s1.rend())//当没到s1.rend()位置时
{cout << *it1 << " ";//输出当前位置的值++it1;//前移
}
④ 范围for
范围for,又称“语法糖”,它是在c++11时才出来的。
范围for的底层本质也是迭代器。
- 先演示下范围for的用法
第一种,可以用auto
让编译器来推导类型。
关于auto,可以到这篇文章中进行了解
第二种,自己给出类型。
前面说了,范围for本质还是迭代器,上面代码中遍历s1
,实际是将*it1
的值赋值到x
中。下面来看下底层
string类对象的修改操作
接口 | 使用说明 |
---|---|
push_back() | 在字符串后尾插字符c |
append() | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串末尾追加字符串str |
c_str()(重点) | 返回c格式的字符串 |
find() | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
① push_back()
在字符串后尾插字符c
- 接口演示
string s1("hello");
s1.push_back('w');
for (char x : s1)
{cout << x;
}
②append()
在字符串后追加一个字符串
string s1("hello");
s1.append("world");
for (char x : s1)
{cout << x;
}
③operator+= (重点)
在字符串末尾追加字符串、字符或者一个对象
string s1("hello");
s1 += "world";
for (char x : s1)
{cout << x;
④c_str()(重点)
返回c格式的字符串
string s1("hello");
cout << s1.c_str() << endl;
⑤find()
从字符串pos位置开始往后找字符c、字符串s或者一个对象,返回该字符在字符串中的位置
- 代码演示
⑥substr
在str中从pos位置开始,截取n个字符,然后将其返回
- 接口演示
string s1("hello world");
string s2 = s1.substr(0,5);
for (auto x:s2)
{cout << x;
}
string类非成员函数
接口 | 使用说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 流插入重载 |
operator<< (重点) | 流提取重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
①operator+
在字符串后拼接字符、字符串或者对象
operator+与operator+=
的区别是前者不会影响自身,后者会影响到自身
②operator>> 、operator<< (重点)(重点)
**③getline **
从流中获取一行放到字符串中
- 接口演示
getline
与流插入的区别
流插入遇到空格就会停止,也就是说,如果要输入hello world
,那么它只会接收到hello
这部分,但是getline
则会全部都接收到
vs和g++下string结构的说明(了解即可)
vs下stirng的结构
1.当字符串长度小于16时,使用内部固定的字符数组来存放
2.当字符串长度大于等于16时,从堆上开辟空间
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的 容量 最后:还有一个指针做一些其他事情。 故总共占16+4+4+4=28个字节。
union _Bxty{
// storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing} _Bx;
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个 指针,该指针将来指向一块堆空间,内部包含了如下字段:
-
空间总大小
-
字符串有效长度
-
引用计数
-
指向堆空间的指针,用来存储字符串。
struct _Rep_base{size_type
size_type
_Atomic_word
};