构造函数
void test1()
{
string s1;//不传参
cout << s1 << endl;
string s2("123456");
cout << s2 << endl;
string s3(s2);
cout << s3 << endl;
string s4(s2, 1, 5);
cout << s4 << endl;
string s5("123456", 2);
cout << s5 << endl;
string s6(8, 'c');
cout << s6 << endl;
}
int main()
{
test1();
return 0;
}
第三种情况最后一个是缺省值,如果没有传第三个参数默认从pos位置开始一直复制到最后
赋值
void test3()
{
string s1="hello";
const string& s2="world";//隐式类型转换
}
size和capacity
void test2()
{
string s1("111111");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << s1.max_size() << endl;
}
int main()
{
test2();
return 0;
}
max_size没什么用,实际上也不会扩容到这么多空间,其实length在这里和size的用、作用是一致的,但length只能用于stl的部分容器,所以我们之后统一用size
[ ]运算符
我们可以直接使用下标来访问
for (int i = 0; i < s1.size(); i++)
{
//cout << s1.operator[](i) << " " ;
cout << s1[i] << " ";
}
两者是等价的
迭代器:iterator
begin:任何容器返回第一个数据位置的iterator
end:任何数据返回最后数据的下一个位置的iterator
迭代器第二种情况是当string的对象是常量时,迭代器指向的数据不能写(但迭代器是可以修改的)
遍历string:
void test4()
{
string s1("hello world");
//遍历方式1,下标
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
//遍历方式2,迭代器
//string::iterator j = s1.begin();
auto j = s1.begin();
while (j != s1.end())
{
//(*j) += 3;可以修改值
cout << *j ;
j++;
}
cout << endl;
//遍历方式3,范围for,底层来看,还是迭代器
for (auto e : s1)
{
cout << e;
}
cout << endl;
}
const修饰后的遍历
void test5()
{
const string s1("hello world");
string::const_iterator j = s1.begin();
//auto j = s1.begin();
while (j < s1.end())
{
cout << *j;
j++;
}
cout << endl;
}
rbegin和rend
本质上和begin和end相同,但是是逆置后的,用法也相似
void test6()
{
string s1("hello world");
string::reverse_iterator j = s1.rbegin();
while (j != s1.rend())
{
cout << *j;
j++;
}
}
排序
按字典序排序
void test7()
{
string s1("hello world");
sort(s1.begin(), s1.end());//对对应的位置排序
cout << s1;
}
+=
类似于c语言中的strcat,但这里就不用考虑s1能不能存的下的问题了
void test8()
{
string s1("11111");
string s2("22222");
s1 += s2;
cout << s1;
}
append:
这里结构和之前的构造是一致的,功能是进行尾插一段字符串
insert:
void test9()
{
string s1("tired");
string s2("222222");
//单个字符
char sh = 'x';
s1.insert(s1.begin(), sh);
cout << s1 << endl;
//迭代器实现
s2.insert(s2.begin(), s1.begin(), s1.end());
cout << s2 << endl;
}
insert的处理起来时间复杂度是O(N),实际上也很少用这个函数
void test10()
{
string s1("hello world");
//s1.erase(1,2);
s1.erase(s1.begin(), s1.end());
cout << s1 << endl;
}
erase:
erase效率和insert差不多,一种迭代器实现,一种是从pos位置开始删除len长度个字符
resize:
reserve:
void test12()
{
string s1("hello world");
//提前开好空间
s1.reserve(63);
cout << s1.capacity() << endl;
s1.resize(20,'x');
cout << s1.capacity() << endl;
}
resize的n比capacity大时,capacity也会自动扩容,如果第二个参数是字符,会自动补充,如果没有,会补充空字符
c_str()
void test13()
{
//c_str兼容c
string file("test.cpp");
FILE* out = fopen(file.c_str(), "r");
char ch= fgetc(out);
while (ch != fgetc(out))
{
cout << ch;
ch = fgetc(out);
}
因为C++兼容C,所以进行读写文件的时候有c_str这个函数
find和rfind
简单来说,find是从前往后找到第一次发现的字符,rfind是从后往前找
find_first_of,find_last_of
与find和last相比,这两个是找一个字符串中任意一个字符匹配的位置,第一个是从前往后找,第二个是从后往前找
如下一段代码,windows和linux下都能查找对应代码路径和文件
void SplitFilename(const string& str)
{
cout << "Splitting: " << str << '\n';
size_t found = str.find_last_of("/\\");
cout << " path: " << str.substr(0, found) << '\n';
cout << " file: " << str.substr(found + 1) << '\n';
}
int main()
{
std::string str1("/usr/bin/man");
std::string str2("c:\\windows\\winhelp.exe");
SplitFilename(str1);
SplitFilename(str2);
return 0;
}
substr:截取一段子串
getline
可以具有cin的功能,但是有cin遇到空格就不再提取,而getline是遇到换行不再提取,也可以指定特殊字符
to_string:将自定义类型转换为string类型
string的capacity扩容
vs上是1.5倍扩容,capacity比实际空间少一个,有一个多的是预留给'\0'的
当字符串长度小于16时,使用内部固定的字符数组来存放 当字符串长度大于等于16时,从堆上开辟空间
void test17()
{
string s;
size_t sz = s.capacity();
cout << "initial capacity " << sz << endl;
for (int i = 0; i < 100; i++)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz<<endl;
}
}
}