目录
string类简介
string类的常用接口说明
string类对象的常见构造
string类对象的访问及遍历操作
operator[ ]
begin + end
rbegin + rend
string类简介
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string string;
- 不能操作多字节或者变长字符的序列。
- 在使用string类时,必须包含#include<string>以及using namespace std;
string类的常用接口说明
string类对象的常见构造
C++98定义了多个构造函数,但是我们最常用的只有一下三种:
(constructor)函数名称 功能说明 string() (重点) 构造空的string类对象,即空字符串 string(const char* s) (重点) 用C-string来构造string类对象 string(size_t n, char c) string类对象中包含n个字符c string(const string&s) (重点) 拷贝构造函数
具体实现方法如下图所示:
#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
// 常用
string s1; // 构造空的string类对象s1
string s2("hello world"); // 用C格式字符串构造string类对象s2
string s3(s2); // 拷贝构造s3
cin >> s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
}
其次,string类依然遵守隐式类型转换原则,在引用对象时,需要加上const修饰:
void push_back(const string& s)
{
//.......
}
void test_string2()
{
string s1 = "hello world";
// 隐式类型转换
//这里s2引用的是临时变量,临时变量具有常性,需要用const修饰
const string& s2 = "hello world";
构造
//string s3("hello world");
//push_back(s1);
//以后尾插不用先构造再尾插,直接尾插就可以
push_back("hello world");
}
s1这样写也是可以的,C++98为我们写了赋值运算符重载,可以直接使用。
string类对象的访问及遍历操作
函数名称 功能说明 operator[ ](重 点) 返回pos位置的字符,const string类对象调用 begin + end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 范围for C++11支持更简洁的范围for的新遍历方式
operator[ ]
string类中operator[ ] 成员函数类似下面代码,引用返回减少一次拷贝构造,也能通过别名修改返回的对象,assert保证了如果传进去的 i 超过了size直接断言报错。
class string
{
public:
// 引用返回
// 1、减少拷贝
// 2、修改返回对象
char& operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
注意:string的类成员对象,如果要统计string变量的大小,要计算类成员变量的内存大小,按照内存对齐原则(32位系统对齐系数是4)即一个string变量大小是12(如图所示的类成员变量总计)而不是算字符串中字符个数,字符的个数与string变量本身内存大小无关。
void test_string3()
{
string s1("hello world");
s1[0] = 'x'; //'h'修改为'x'
cout << s1.size() << endl;
//cout << s1.length() << endl;
//输出11 不包含\0
//lenth和size等价,但是为了后续其他数据计算方便,统一写size
for (size_t i = 0; i < s1.size(); i++)
{
s1[i]++; //字符串中每个元素+1(ASCII)
}
// 越界检查 s1[20]; //assert(i < _size);
for (size_t i = 0; i < s1.size(); i++)
{
//cout << s1.operator[](i) << " "; 显示调用
cout << s1[i] << " "; //遍历字符串打印每个字符
}
cout << endl;
const string s2("hello world");
// 不能修改
//s2[0] = 'x';
}
如果string被const修饰,还可以将第一个元素修改为'x'吗?如果不能,为什么呢?看下图:
s1,s2优先匹配相似度高的函数,即遵循模板参数的匹配原则。
begin + end
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器,begin和end可以实现字符串的遍历。
void test_string4()
{
string s1("hello world");
// 遍历方式1:下标+[]
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
//遍历方式2: 迭代器
//auto it1 = s1.begin(); //这样写前提是你知道他是什么类型
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
*it1 += 3;
cout << *it1 << " ";
++it1; //控制it1移动
}
cout << endl;
//遍历方式3: 范围for
// 底层角度,他就是迭代器
for (auto& e : s1)
{
e++; //想要修改加上引用
cout << e << " ";
}
cout << endl;
}
这样就有三种方式可以遍历数组, 我们可以将迭代器想象成指针,如下图所示:
从begin()开始,到end()就停止遍历,可以修改字符串里的值。
void test_string5()
{
const string s1("hello world");
//string::const_iterator it1 = s1.begin();
auto it1 = s1.begin();
while (it1 != s1.end())
{
// 不能修改
//*it1 += 3;
cout << *it1 << " ";
++it1;
}
}
那么加上const修饰应该怎么实现权限的平移呢?另外const_iterator为什么中间要加短下划线?如下图所示:
注意:在编写程序时,const_iterator中间要写下划线
rbegin + rend
rbegin记录字符串尾元素,rend记录字符串头元素,两者为反向迭代器,共同使用可以实现数组的逆序访问。
void test_string6()
{
const string s1("hello world");
//string::const_reverse_iterator cit1 = s1.rbegin();
auto cit1 = s1.rbegin();
while (cit1 != s1.rend())
{
// 不能修改
//*cit1 += 3;
cout << *cit1 << " ";
++cit1;
}
cout << endl;
string s2("hello world");
string::reverse_iterator it2 = s2.rbegin();
//auto it2 = s2.rbegin();
while (it2 != s2.rend())
{
*it2 += 3;
cout << *it2 << " ";
++it2;
}
cout << endl;
}
在图中所示:此处依旧遵循模板参数的匹配原则
此处rbegin从右向左遍历至rend,++往左移动, 编译器实现逻辑如下图所示: