
༺ 个人主页 · 纪念229 ༻
༒专栏目录:《算法》༒
༒专栏目录:《C++》༒
༒专栏目录:《MySQL数据库》༒
༒专栏目录:《前端开发》༒
༺世上本没有路,走的人多了自然就有了༻
本文章接着上节讲述C++标准库里的string类,希望对你有所帮助
讲解的是本人认为重要的,更详细的可以看代码注释,这些东西复现之后建议调试一下
文章目录
1.test_string2()补充
c
void test_string2()
{
string s1("hello world");
const string s2("hello world");
s1[0]++;
////这里不要想着是++重载它不是类是字符元素
////纯属改ascll值
//s2[0]++;
cout << s1 << endl;
////遍历修改
////1、下标 + [] (小众)
//// s1的每个字符都++
for (int i = 0; i < s1.size(); i++)
////s1是string类型内置size函数(求字符串个数)
{
s1[i]++;
}
cout << s1 << endl;
////[begin(),end())
//// begin()指向第一个有效字符(下标0)
//// end()指向最后一个有效字符的下一位(末下标的下一位(不存在))
////
////
////
////2、迭代器 (所有容器主流遍历 + 加修改方式)
string::iterator it1 = s1.begin();
// //string::iterator(一个类型名)看作迭代器(智能指针)
// //s1.begin()调用s1的成员函数返回指向第一个字符的地址
while (it1 != s1.end())
//s1.end()调用s1的成员函数返回指向第最后字符的下一个东西的地址
{
(*it1)--;
++it1;
//因为it1是类类型所以这里调用了成员函数(解引用重载和加加重载)
}
cout << s1 << endl;
//string s = "你好";
//这个东西(s)在调试上看不到你好
//不用纠结,s实际上里面就是你好只是在监视窗口看不到想要看直接在终端打印即可
vector<int> v;
//用vector模板要带头文件#include<vector>
//vector(变长数组)(初始下标还是0)
//下面都是vector里的成员函数
v.push_back(100000);
v.push_back(2);
v.push_back(3);
vector<int>::iterator it2 = v.begin();
while (it2 != v.end())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
//string类和下面的vector、list不一样它们调用的是类模板string调用的就是类,string就是vector<char>的进阶版,可以调用的成员函数更多
//string对象比vector<char>对象可视化更好在调试器里string对象看到的是"hello world"在vector<char>对象看到的是'h' 'e' 'l'......
list<int> lt;
//用list类模板要头文件#include<list>
//list双向链表
//下面就是list类可以用的成员函数
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
list<int>::iterator it3 = lt.begin();
while (it3 != lt.end())
{
cout << *it3 << " ";
it3++;
}
cout << endl;
//string调用头文件后用的是类
//vector和list调用头文件后用的是类模板
//这些东西都是容器
//迭代器
//类/模板类::iterator 这个iterator就是迭代器(指针/类)调用就要用类/类模板域
//这些类利用迭代器改变对象里成员变量里面的数据
//string和vector的迭代器是指针 list迭代器是类
//迭代器的意义
//1:统一类似的方式遍历修改容器
//2:算法脱离具体底层结构结构,和底层结构解耦,
//算法独立模板实现针对多个容器处理
reverse(s1.begin(), s1.end());
reverse(v.begin(), v.end());
reverse(lt.begin(), lt.end());
//reverse(起始指针,末指针)
//作用将该对象的成员变量中的数据根据始末指针进行交换
//头文件#include<algorithm>
auto x = 10;
auto y = 10.1;
cout << x << endl;
cout << y << endl;
//auto类型会推导相关类型
//不是模板
int& z = x;
auto& m = z;
m++;
auto p1 = &x;
//只能是指针
auto* p2 = &x;
//auto可以推导大多是类型(内置类型、指针类型、类类型等)
//这上面两个一个推导的是int*一个是int
//3、范围for C++11 语法糖
//自动取容器数据赋值给e
//自定判断结束
//自动迭代
//本质底层也会替换成迭代器, auto e = *迭代器
//for(char& e : s1)
for (auto& e : s1)
{
//e是迭代器的解引用
e--;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
int array[] = { 1,2,3,4,5 };
//我们看到int类型的数组要求个数要sizeof(数组名)/sizezof(数组名[0])
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
cout << array[i] << " ";
}
cout << endl;
for (auto e : array)
{
cout << e << " ";
}
cout << endl;
//由此可见打印所有数组值用范围for更方便
}
c
list<int> lt;
//用list类模板要头文件#include<list>
//list双向链表
//下面就是list类可以用的成员函数
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
list<int>::iterator it3 = lt.begin();
while (it3 != lt.end())
{
cout << *it3 << " ";
it3++;
}
cout << endl;
//string调用头文件后用的是类
//vector和list调用头文件后用的是类模板
//这些东西都是容器
//迭代器
//类/模板类::iterator 这个iterator就是迭代器(指针/类)调用就要用类/类模板域
//这些类利用迭代器改变对象里成员变量里面的数据
//string和vector的迭代器是指针 list迭代器是类
//迭代器的意义
//1:统一类似的方式遍历修改容器
//2:算法脱离具体底层结构结构,和底层结构解耦,
//算法独立模板实现针对多个容器处理
list也是一种类模板我们可以把它看作是双向链表(当然成员变量不止双向链表)
c
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
这里的push_back()与string没啥区别就是在最后一个元素后加一个数据
c
list<int>::iterator it3 = lt.begin();
while (it3 != lt.end())
{
cout << *it3 << " ";
it3++;
}
list类型的迭代器不是指针是类用法就是改变成员变量数据与string差不多
C++标准库里string是一种写好的类字符数组,vector是类模板可以是大多数类型的数组,list类模板是一种双向链表(类模板的实例化决定它是啥类型的东西)
c
reverse(s1.begin(), s1.end());
reverse(v.begin(), v.end());
reverse(lt.begin(), lt.end());
这里reverse()是将这些类的数据按照最中间为基准对称交换
reverse(迭代器(指向第一个元素),迭代器(最后指向的数据的下一个位置))
reverse()的头文件是#include
c
auto x = 10;
auto y = 10.1;
cout << x << endl;
cout << y << endl;
//auto类型会推导相关类型
//不是模板
这里的 auto 类型会自动推导类型:x 右边是 int,那么 auto 就是 int;y 右边是 double 类型,auto 就是 double 类型。类型
c
auto p1 = &x;
//只能是指针
auto* p2 = &x;
auto会根据右边的类型具体推导
p1右边是int类型 auto就是int
p2右边是int类型但是左边有个那auto就是int类型拼起来就是int*
c
for (auto& e : s1)
{
//e是迭代器的解引用
e--;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
范围for
结构
c
for(类型名 变量 : 数组名)
{
//..........
}
范围for将某类型数组从一个数据开始读起每次取一个元素复制给变量一直读到最后一个元素
这里类型名加引用的话就会改变对象的数据
c
int array[] = { 1,2,3,4,5 };
//我们看到int类型的数组要求个数要sizeof(数组名)/sizezof(数组名[0])
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
cout << array[i] << " ";
}
这里就讲int类型数组求元素个数无法像字符数组用strlen函数计数要用整个数组的字节整除单个元素的字节
sizeof(array)/sizeof(array0)不用头文件
在sizeof里面的数组名指的不是首元素的地址指的是整个数组
范围for的方便就是在数组元素都要遍历的时候用
2.test_string3()
c
void test_string3()
{
string s1("123456");
const string s2("hello world");
//[begin(),end())
//迭代器(所有容器主流遍历 + 修改方式)
// s1的每个字符都--
string::iterator it1 = s1.begin();
//string::iterator = s1.begin iteraotr是类型名后面要接 变量 ();
while (it1 != s1.end())
{
(*it1)--;
++it1;
}
cout << s1 << endl;
string::const_iterator it2 = s2.begin();
//string::iterator it2 = s2.end();因为s2是const string类型所以要用它的迭代器要用const_iterator类型名,但实际上不能修改的东西是string对象不是迭代器
while (it2 != s2.end())
{
//(*it2)--;不能修改
++it2;
}
cout << s2 << endl;
string::reverse_iterator it3 = s1.rbegin();
while (it3 != s1.rend())
//while (it3 != it3.rend())//it3不可以调用成员函数
{
cout << *it3 << " ";
it3++;
}
cout << endl;
//reverse_iterator作用:将迭代器的--变为向后走++变为向前走
//rbegin()指向容器最后一个元素
//rend()指向容器第一个元素的前一个位置
string::const_reverse_iterator it4 = s2.rbegin();
//这里因为s2是const string类型所以要用这个迭代器要用const_reverse_iterator
while (it4 != s2.rend())
{
//不能修改
//*it4 = 'x';
cout << *it4 << " ";
++it4;
}
cout << endl;
//这种迭代器适合反向打印
for (auto ch : s2)
{
cout << ch << " ";
}
cout << endl;
}
const string s2("hello world");
c
string::const_iterator it2 = s2.begin();`
while (it2 != s2.end())
{
//(*it2)--;不能修改
++it2;
}
这里s2是const string类型我们规定该类型用迭代器时用const_iterator类型且利用迭代器不能改变对象的数据
string::reverse_iterator it3 = s1.rbegin();
这里的string里的函数rbegin()指的是最后一个元素用这个赋值给reverse_iterator类型
c
while (it3 != s1.rend())
//while (it3 != it3.rend())//it3不可以调用成员函数
{
cout << *it3 << " ";
it3++;
}
这里rend()指向第一个元素前一个位置
这里的代码实现就是逆向打印
string::const_reverse_iterator it4 = s2.rbegin();
这里因为s2是 const string类型所以用rend()赋值的类型是const_reverse_iterator类型
3.test_string4()
c
void test_string4()
{
string s1("123456");
cout << s1.max_size() << endl;
//成员函数max_size()作用:这个string对象最多可以容纳多少个字符(与对象内容无关)
//均不包含\0
cout << s1.size() << endl;;
cout << s1.capacity() << endl;
//capacity()返回当前能装的字符个数
//size()当前有多少字符
//capacity()当前能装多少字符 这两个不数string对象自带的\0
//字符多了会扩容
//max_size()最多能装多少字符
string s2;
//确定需要多少空间,提前开好,避免扩容,提高效率
s2.reserve(100);
//reserve(扩容的字符个数)
size_t old = s2.capacity();
cout << "capacity:" << old << endl;
//容纳的字符个数为啥不是100而是111(受C++标准库不同影响扩容的字符数不同但是一定大于等于我们所需求的字符容量)
for (size_t i = 0; i < 100; i++)
{
s2.push_back('x');
//push_back()作用在字符串后面追加一个字符,如果字符串为空就在下标为0的位置追加一个字符,不为空就在字符末追加一个字符,push_back()不能追加一个字符串
if (s2.capacity() != old)
{
cout << "capacity" << s2.capacity() << endl;
old = s2.capacity();
}
}
cout << s1 << endl;
s1.clear();
//clear()清空对象里的字符
cout << s1 << endl;
//不会缩容,缩容要重新开空间,代价很大
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
//s2.clear();
for (size_t i = 0; i < 50; i++)
{
s2.pop_back();
//pop_back()减该对象最后一个字符
}
s2.shrink_to_fit();
//shrink_to_fit()这个是将该对象能容纳的字符个数变为已有字符个数相同或多一点
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
}
cout << s1.max_size() << endl;
这里max_size()函数在string类里意思是对象最多能容纳多少数据空间
c
cout << s1.size() << endl;;
cout << s1.capacity() << endl;
string类里的size()、capacity()函数一个是查询该对象目前有效数据为几一个是查询该对象目前可容纳空间为几(随着有效数据的增长到了与容量相同的时候再加入有效数据capacity就会扩容)
s2.reserve(100);
string类里的reserve就是提前扩容(capacity)(不能缩容)提高效率
受C++标准库的影响reserve扩容数据不一定我们想扩的但一定与其差不多并一定大于等于我们想扩容的空间
s1.clear();
string里的clear()函数是将对象的数据全部清空
s2.pop_back();
string里的pop_back是将该对象最后一个元素移除
s2.shrink_to_fit();
string里的shrink_to_fit()函数就是将该对象可容纳的空间变的与对象的有效数据相同或多一点点
4.test_string5()
c
void test_string5()
{
string s1("123456");
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl;
//扩容
s1.reserve(100);
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl;
//缩容 不靠谱
s1.reserve(3);
//拿reverse缩容什么都不会发生,用shrink_to_fit()可以缩容但是不能小于等于已有字符个数(且该函数没有实参)
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl << endl;
string s2("123456");
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
//插入数据,让size到n个
//n > capacity > size;
s2.resize(20, 'x');
//resize(设置有效个数,'x'(当前有效个数不足设置有效个数就用'x'补到与设置有效个数相同))作用:设置有效个数如果当前有效个数比设置有效个数少就用'x'在字符末补到与设置有效个数相等为止
cout << s2 << endl;
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
//capacity > n > size;
//s2.resize(25, 'x');
s2.resize(25);//插入的是\0(插入的是\0调试是看起来没区别因为\0看不见)
s2.push_back('y');
//在C++中string对象里'x'与'y'之间放许多'\0'打印出来的还是xy
//在c语言中这样一般遇到'\0就结束会打印x'
cout << s2 << endl;
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
//删除数据
//n < size(resize可以加有效数据也可以删除有效数据)
s2.resize(5);
cout << s2 << endl;
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
}
s1.reserve(3);
这里的对象原本的capacity(容量比3大)reserve不支持缩容如果缩容什么都不会改变
s2.resize(20, 'x');
string类里的resize函数(resize(有效数据的个数, 有效数据不足用该字符补))作用是设置有效数据若是当前有效个数不足用函数里的用的字符补到有效数据(size)足为止
s2.resize(25);//插入的是\0(插入的是\0调试是看起来没区别因为\0看不见)
resize只有一个实参时设置有效个数不足用'\0'补满(不是'0')
s2.resize(5);
若是输入的数字比对象的有效数据小就会将后面超出的有效数据删除(这里是删除有效数据(size)不是缩容(capacity))
5.test_string6()
c
void test_string6()
{
string s1("hello world");
s1[0]++;
cout << s1 << endl;
//越界检查,断言
//s1[15];//s1.operator[](15)这里s1是类的对象我们得到一个字符是因为调用了运算符重载(有断言作用)这里越界了运行停止
s1.at(0)--;
//at(n)--/++将n下标的数据sacll码值减一或加一
cout << s1 << endl;
//越界抛异常
//s1.at(15);
//s1[15] s1.at(15)这两个越界一个报断言一个报异常都会使运行结束
int a[10];
//a[15] = 1;//*(a + 15) = 1这里就是越界编译失败上面两个调用的是成员函数报的断言和异常
}
//s1[15];//s1.operator[](15)这里s1是类的对象我们得到一个字符是因为调用了运算符重载(有断言作用)这里越界了运行停止
因为s1是类的对象所以s1X是调用了运算符重载若是越界该重载会编译报错
s1.at(0)--;
string类里的函数at(下标)--/++作用是将对象数组的某下标上的数据加一或减一
cout << s1 << endl;
这里的类对象调用了 << string里的运算符重载将该类数组数据打印出来
c
int a[10];
//a[15] = 1;//*(a + 15) = 1这里就是越界编译失败上面两个调用的是成员函数报的断言和异常
这里a是个普通的整型数组越界编译失败之前的类对象一个X.x报的断言一个是X.at(X);报的是异常当它们越界时
文章到此就到一段落,希望对你有所帮助,感谢观看!