目录
string (const string& str, size_t pos, size_t len = npos);
string& append (const string& str);
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s);
string& append (const char* s, size_t n);
template string& append (InputIterator first, InputIterator last);
1.知识回顾
之前在C++ Contest专栏提到过string的使用
CC12.【C++ Cont】string类字符串的创建、输入、访问和size函数
CC13.【C++ Cont】初识string类字符串的迭代器
CC14.【C++ Cont】string类字符串的push_back、pop_back、字符串+=与+运算和insert
2.串联类和对象的知识重新理解
上方提到的文章讲的比较浅,只是介绍了怎么用,本文将用类和对象的思想来理解
以https://legacy.cplusplus.com/reference/string/string/?kw=string网为线索:
构造函数
下面只讲常用的
string();
无参数传递,即默认构造,也称无参构造
string (const string& str);
传参的类型为const string,为拷贝构造
string (const string& str, size_t pos, size_t len = npos);
传参的类型为const string,为拷贝构造,只不过是拷贝构造str的子串,显然第3个参数是缺省参数,如果不写,默认从pos位置一直截取的string风格的字符串的结尾
string (const char* s);
const char*为C语言风格的字符串类型,因此为使用C风格的字符串构造
string (size_t n, char c);
使用n个字符c来构造
append和push_back
https://legacy.cplusplus.com/reference/string/string/append/
append v.追加 即向原string类字符串后追加字符串,作用类似push_back,只不过push_back只能追加字符
有关push_back成员函数的简单使用参见CC14.【C++ Cont】string类字符串的push_back、pop_back、字符串+=与+运算和insert文章
此外:如果string的空间不够,append或push_back会自动扩容,(具体的扩容策略和编译器的处理有关,没有统一的规定),C语言strcat,不能自动扩容且找\0耗时
string& append (const string& str);
向原string类字符串后追加string类字符串(类似push_back)
string& append (const string& str, size_t subpos, size_t sublen);
向原string类字符串后追加string类字符串str的子字符串
string& append (const char* s);
向原string类字符串后追加C语言风格的字符串
string& append (const char* s, size_t n);
向原string类字符串后追加C语言风格的字符串的前n个字符
代码示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str("teststring");
str.append("abcdef", 3);
cout << str << endl;
return 0;
}
运行结果:
template <class InputIterator>
string& append (InputIterator first, InputIterator last);
向原string类字符串后追加范围为[first,last)的一串字符
代码示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1("teststring");
string str2("000abc111");
str1.append(str2.begin()+3,str2.begin()+6);
cout << str1 << endl;
return 0;
}
运行结果:
operator+=
参见CC14.【C++ Cont】string类字符串的push_back、pop_back、字符串+=与+运算和insert文章
operator[ ]
https://legacy.cplusplus.com/reference/string/string/operator[]/
给了两种重载形式,一个可以修改字符串,一个不能修改字符串
可做如下测试:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1("helloworld");
const string str2("teststring");
str1[1];
str2[1];
return 0;
}
写法等价为:使用点操作符调用成员函数
str1.operator[](1);
str2.operator[](2);
从地址上看,调用operator[]的地址不同,因此是不同的重载函数:
如果从operator[]函数的定义上来看:
str1[1]的operator[]没有有const修饰:
str2[1]的operator[]有const修饰:
理解[ ]本质是解引用
代码示例:可以像数组一样访问
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str("helloworld");
for (size_t i = 0; i < str.size(); i++)
cout << str[i];
return 0;
}
运行结果:
上方代码能否访问到\0?
方法1:下条件断点后监视
监视窗口查看:
str[i]解引用后是最后一个字母d,不是\0
方法2:反汇编后看内存,手动查找
Debug+x86环境下,for循环的反汇编代码:
发现ebp-3Ch和ebp-30h高频出现,由mov dword ptr [ebp-3Ch],0 猜测这是为变量i赋初值,因此[ebp-3Ch]存的是i的值, 由cmp dword ptr [ebp-3Ch],eax和jae 00540BDD猜测这是i < str.size()的条件判断,因此猜测eax临时存储的是str.size的值,可以看看寄存器:
0x0000000A=十进制的10,不带\0的helloworld恰好占10个字节,因此不会访问到\0
方法3:直接监视窗口看封装好的string类的str
备注:如果想强制打印\0可以将i < str.size()改成i <= str.size()
循环后再打印一个#检测\0有没有占位:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str("helloworld");
for (size_t i = 0; i <= str.size(); i++)
cout << str[i];
cout << "#";
return 0;
}
Visual Studio 2022查看控制台窗口:
发现\0不显示,且不占位,因为VS认为str[i]超出界限,因此没有为\0占位
Dev C++查看控制台窗口:
发现\0不显示,且占位
Ubuntu Linux g++运行结果:
发现\0不显示,且不占位
发现不同平台的处理方法不同
缺点
Operator[ ]只有连续的空间才能使用,即非线性结构不能使用[ ]访问,但一些线性结构和非线性结构都可以使用迭代器访问,例如链表:
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
list<int> ls;
ls.push_back(1);
ls.push_back(3);
ls.push_back(13);
ls.push_back(0);
ls.push_back(43);
ls.push_back(546);
ls.push_back(9);
for (auto i = ls.begin(); i != ls.end(); i++)
{
cout << *i << "-->";
}
cout << "NULL";
return 0;
}
运行结果: