C++ Prime 第九章 顺序容器
2023-04-03~2023-04-25
顺序容器概述
容器库概述
9.1节练习
练习9.1:
(a)list
(b)deque
(c)vector
9.2节练习
练习9.2:
list<deque<int>> myDeque;
9.2.1节练习
练习9.3:
- 两个迭代器begin、end指向同一个容器中的元素
- 可以通过对begin递增使begin达到end
练习9.4:
#include<vector>
#include<iostream>
using namespace std;
bool getNum(vector<int>& vec)
{
vector<int>::iterator end = vec.end();
int result = 7;
for (vector<int>::iterator begin = vec.begin(); begin < end; ++begin)
{
if (*begin == result)
{
return true;
}
}
return false;
}
int main()
{
vector<int> myInt = { 1, 2, 3, 4, 5, 6, 7, 8 };
bool resultBool = getNum(myInt);
cout << resultBool << endl;
system("pause");
return 0;
}
练习9.5:
#include<vector>
#include<iostream>
using namespace std;
vector<int>::iterator getNum(vector<int>& vec)
{
vector<int>::iterator end = vec.end();
int result = 8;
for (vector<int>::iterator begin = vec.begin(); begin < end; ++begin)
{
if (*begin == result)
{
return begin;
}
}
return end;
}
int main()
{
vector<int> myInt = { 1, 2, 3, 4, 5, 6, 7, 8 };
vector<int>::iterator resultIter = getNum(myInt);
cout << *resultIter << endl;
system("pause");
return 0;
}
9.6练习:
- list不支持随机访问,所以该类型的迭代器无法通过大于、小于号来判断(可以改为==号)
9.2.2节练习
练习9.7:
iterator、auto
练习9.8:
读取:auto、iterator,写入:auto&、reference、pushback()、insert()
begin和end成员
- 当auto与begin、end结合使用时,获得的迭代器类型依赖容器类型,容器如果时const就获得const_iterator,如果使用的是cbegin和cend也是获得const_iterator,当不需要写访问时应该使用const版本的
9.2.3节练习
练习9.9:
begin:可以通过迭代器修改其中的元素值,cbegin:不可以通过迭代器修改其中的元素值
练习9.10:
iterator:it1,const_iterator:it2、it3、it4
容器定义和初始化
- 当创建一个容器为另一个容器的拷贝时,两个容器的类型和元素类型必须匹配
- 使用迭代器范围来创建容器的拷贝时,只要迭代器范围内的元素类型可以转化成另一个容器的元素类型即可
- 只有顺序容器的构造函数(array除外)才接收大小参数,关联容器不支持
标准库array具有固定大小
- 与内置数组一样,标准库中array的大小也是类型的一部分,当指定一个array时,除了元素类型,还要指定容器大小
array<int, 42> arr;
- 因为容器的大小是array类型的一部分,所以上诉顺序容器array不支持普通的构造函数(提供容器大小参数、默认值)
- 与内置数组不同的是,我们可以对array进行对象赋值或者拷贝操作(内置数组不行),与其他容器类型一样除了元素类型需要一致,array的大小也必须相同,因为array的大小是类型的一部分
#include<array>
#include<iostream>
using namespace std;
int main()
{
int dig[5] = { 1, 2, 3, 4, 5 };
int cpy[5] = dig; // 报错,内置数组不允许拷贝、赋值操作
array<int, 5> arr = { 1, 2, 3, 4, 5 };
array<int, 5> cpy_arr = arr; // array可以进行拷贝、赋值操作
system("pause");
return 0;
}
9.2.4节练习
练习9.11:
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec1; // 空的vector类型
vector<int> vec2(1); // 存在一个int类型的数据,数据内容为1
vector<int> vec3(10, 1); // 存在10个int类型的数据,数据内容为1
vector<int> vec4 = vec3; // 存在10个int类型的数据,数据内容为1
vector<int> vec5(vec2); // 存在一个int类型的数据,数据内容为1
vector<int> vec6(vec3.begin(), vec3.end()); // 存在10个int类型的数据,数据内容为1
vector<int> ve7 = { 1, 2, 3 }; // 存在3个int类型的数据,数据内容为1, 2, 3
system("pause");
return 0;
}
练习9.12:
从构造函数的结构来看:
接收一个容器创建其拷贝的构造函数:需要两个容器的类型相同且容器中的元素类型也相同
接收两个迭代器创建拷贝的构造函数:只需要迭代器范围内的元素类型可以转化成另一个容器中的元素类型即可
从拷贝后的对象来看:
接收一个容器创建其拷贝的构造函数:两个容器数据类型、元素值都相等
接收两个迭代器创建拷贝的构造函数:拷贝后的容器可能是另一个容器的子集(主要看迭代器的范围是否等于被拷贝容器的全部大小)
练习9.13:
#include<vector>
#include<list>
#include<iostream>
using namespace std;
int main()
{
list<int> myList = { 1, 2, 3, 4, 5 };
vector<double> myVector1(myList.begin(), myList.end());
vector<int> myVector2(2, 2);
vector<double> myVector3(myVector2.begin(), myVector2.end());
system("pause");
return 0;
}
赋值和swap
使用assign
- 右边运算对象可能与左边运算对象的大小不同,所以array不支持assign,不允许使用{}进行赋值
#include<vector>
#include<list>
#include<iostream>
using namespace std;
int main()
{
list<string> names;
vector<const char*> oldstyle;
names = oldstyle; // 报错,容器类型不同
// 使用迭代器
names.assign(oldstyle.begin(), oldstyle.end());
list<string> slist1(1);
// 类似使用构造函数
slist1.assign(10, "Hiya!");
// 等价于下述操作
slist1.clear();
slist1.insert(slist1.begin(), 10, "Hiya!");
system("pause");
return 0;
}
使用swap
- swap:交换两个相同类型容器的内容
vector<string> svec1(10);
vector<string> svec2(24);
swap(svec1, svec2);
- 除array外,swap不对任何元素进行拷贝、删除或者插入操作,可以在常数时间内完成,swap之后指向容器的迭代器、引用、指针都不会失效
- 对于string进行swap操作会导致指向string的迭代器、引用、指针失效
- 对于array进行swap会真正交换它们之间的元素,所需的时间和array中元素的数目成正比
9.2.5节练习
- 练习9.14:
#include<vector>
#include<list>
#include<iostream>
using namespace std;
int main()
{
// 构造一个c风格的字符串:以\0结尾的字符数组
char ptr1[] = "c"; // 自动加上\0
char prt2[] = { 'c', '\0' }; // 手动加上\0
const char* ptr3 = "c++"; // 也是c风格的字符串
list<char*> cclist;
for (int i = 0; i < 10; ++i)
{
cclist.push_back(ptr1);
}
vector<string> svectaor(10);
for (auto temp : cclist)
{
svectaor.push_back(temp);
}
for (auto temp : svectaor)
{
cout << temp << endl;
}
system("pause");
return 0;
}
9.2.6容器大小操作
- size:返回容器中元素的数目,forward_list不支持size
- empty:当容器size=0时,返回true
- max_size:返回一个大于等于该容器所能容纳的最大元素数的值
9.2.7关系运算符
- 每个容器都支持相等运算符(==、!=)
- 除无序容器外所有容器都支持关系运算符(>、<、>=、<=)
- 只有容器类型相同并且容器中保存的数据类型也相同的容器才能使用关系运算符
- 两个容器大小相同并且容器中对应位置的元素相同的两个容器相等
- 两个容器大小不同,但较小容器中的对应的元素都等于较大容器中的元素时,较大的容器大于较小的容器
- 两个容器都不是另一个容器的前缀子序列,则它们的结果取决于第一个不相等元素的比较结果
9.2.7节练习
练习9.15:
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec1 = {1, 2, 4};
vector<int> vec2 = {1, 2, 5, 4 };
cout << (vec1 == vec2) << endl;
system("pause");
return 0;
}
练习9.16:
不同容器类型的数据无法比较
练习9.17:
c1和c2不能是无序容器,无序容器不支持<、>
顺序容器操作
- push_back():向容器的尾部添加一个元素(单向链表forward_list、固定数组array不支持push_back)
- push_front():向容器的头部添加一个元素(固定数组array、可变数组vector、字符数组string不支持push_front)
- insert():向容器任意位置添加元素(单向链表forward_list、固定数组array不支持insert)
#include<vector>
#include<list>
#include<iostream>
using namespace std;
int main()
{
// 1、插入单个元素
vector<int> vec = {1, 2, 4};
list<int> lst = {1, 2, 5, 4 };
// 通过insert实现向list、vector头部添加数据
vec.insert(vec.begin(), 0);
lst.insert(lst.begin(), 0);
// 2-1、插入范围元素:接收元素数目和元素值
// 在vec的头部位置添加10个元素,元素值=5
vec.insert(vec.begin(), 10, 5);
// 2-2、插入范围元素:接收一个初始化列表
vec.insert(vec.begin(), { 1, 2, 3, 4, 5 });
// 2-3、插入范围元素:接收一对迭代器
// 书上说:迭代器表示要拷贝的范围,不能指向与目的位置相同的容器
// 实际上在visual2022中这么做没有报错,是编译器的问题吗?有知道的兄弟可以给解释一下
vector<int> vec2 = { 1, 2, 3 };
vec2.insert(vec2.begin(), vec2.begin(), vec2.end());
system("pause");
return 0;
}
使用insert的返回值
- insert返回值:返回指向第一个新加入元素的迭代器
- 通过使用insert返回值实现类似front的效果
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec = { 1, 2, 3, 4 };
auto iter = vec.begin();
while (vec.size() < 10)
{
iter = vec.insert(vec.begin(), 2, 5);
}
for (auto temp : vec)
{
cout << temp << endl;
}
system("pause");
return 0;
}
使用emplace操作
- emplace、emplace_front、emplace_back这些操作构造(直接在容器内构造函数后插入特定的位置)而不是拷贝元素,相对与使用insert、push_back、push_front免去了对象复制和移动操作(生成一个临时拷贝对象,再把临时拷贝对象放入容器内),提高程序效率
#include<vector>
#include<iostream>
using namespace std;
class SalesData
{
public:
SalesData() = default;
SalesData(string SName, int SNo) {};
};
int main()
{
vector<SalesData> mySales;
mySales.emplace_back(); // 调用SalesDate的默认构造函数
mySales.emplace_back("C++Prime", 00001); // 调用SalesDate的有参构造函数
system("pause");
return 0;
}
9.3.1节练习
练习9.18:
#include<deque>
#include<iostream>
using namespace std;
int main()
{
deque<string> myDeque;
string temp;
while (cin >> temp)
{
myDeque.push_back(temp);
}
auto iter = myDeque.begin();
for (auto _ : myDeque)
{
cout << *iter << endl;
++iter;
}
system("pause");
return 0;
}
练习9.19:
#include<list>
#include<iostream>
using namespace std;
int main()
{
list<string> mylist;
string temp;
while (cin >> temp)
{
mylist.push_back(temp);
}
auto iter = mylist.begin();
for (auto _ : mylist)
{
cout << *iter << endl;
++iter;
}
system("pause");
return 0;
}
练习9.20:
#include<list>
#include<deque>
#include<iostream>
using namespace std;
int main()
{
list<int> mylist = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
deque<int> evenDeque;
deque<int> oddDeque;
for (auto temp : mylist)
{
if (temp % 2 == 0)
{
evenDeque.push_back(temp);
}
else
{
oddDeque.push_back(temp);
}
}
cout << "偶数:" << endl;
for (auto evenNUM : evenDeque)
{
cout << evenNUM << endl;
}
cout << "奇数:" << endl;
for (auto addNUM : oddDeque)
{
cout << addNUM << endl;
}
system("pause");
return 0;
}
练习9.21:
向第一个元素之前的位置插入元素
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec = { 1, 2, 3, 4 };
auto iter = vec.begin();
while (vec.size() < 10)
{
iter = vec.insert(vec.begin(), 2, 5);
}
for (auto temp : vec)
{
cout << temp << endl;
}
system("pause");
return 0;
}
练习9.22:
问题1:修改容器长度后会使该容器的迭代器失效,使用全局变量替代迭代器作为循环判断的条件
问题2:if语句没有意义,如果相等就有机会跳出循环,如果不相等就死循环,所以去掉if语句,将insert语句从if语句中搬出来
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> myVector = { 1, 2, 3, 4, 5, 6 };
auto iter = myVector.begin();
int some_val = 10;
size_t midNum = myVector.size() / 2 + 1;
size_t beginNum = 0;
while (beginNum != midNum)
{
iter = myVector.insert(iter, 2 * some_val);
++beginNum;
size_t midNum = myVector.size() / 2 + 1;
}
for (auto temp : myVector)
{
cout << temp << endl;
}
system("pause");
return 0;
}
9.3.2访问元素
- 顺序容器的成员函数front:获取首元素的引用
- 顺序容器的成员函数back(除了forward_list):获取尾元素的引用
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec = { 1, 2, 3, 4, 5 };
if (!vec.empty())
{
// 使用front获取首元素
auto &val1 = vec.front();
cout << val1 << endl;
// 使用begin获取首元素
auto &val2 = *(vec.begin());
cout << val2 << endl;
// 使用back获取尾元素
auto &val3 = vec.back();
cout << val3 << endl;
// 使用end获取尾元素
auto &val4 = *(--(vec.end()));
cout << val4 << endl;
}
system("pause");
return 0;
}
- 顺序容器的成员函数下标([N]):通过下标获取元素的引用(该方法只适用于string、vector、deque、array),如果下标越界,编译器并不会检查该错误,只有当运行时才会报错
- 顺序容器的成员函数at:通过at(N)方法获取下标为N的元素的引用(该方法只适用于string、vector、deque、array),如果下标越界,编译器会抛出out_of_range异常
9.3.2节练习
练习9.23:
它们的值都相同
练习9.23:
vector<int> vec;
vec.at(0);
vec.front();
vec[0];
*vec.begin();
9.3.3删除元素
- array不支持删除元素操作
- pop_front:删除首元素并返回void,(可变数组vector、字符数组string不支持该操作)
- pop_back:删除尾元素并返回void,(单向链表forward_list不支持该操作)
- erase(n):删除指定位置n的元素并返回该元素之后的元素的迭代器
- erase(b,e):删除指定范围内的所有元素并返回该元素之后的元素的迭代器
- clear:删除容器中的所有元素并返回void
- 删除deque除首、尾位置的元素会导致所有迭代器、指针、引用失效
- 指向vector、string中删除点之后的迭代器、指针、引用都会失效
9.3.3节练习
练习9.25
#include<list>
#include<iostream>
using namespace std;
int main()
{
list<int> lst = { 1, 2, 3, 4 };
auto iter1 = lst.begin();
auto iter2 = iter1;
auto iter3 = lst.erase(iter1, iter2);// iter1到iter2直接没有任何元素,返回首元素迭代器
cout << *iter3 << endl; // 打印1,
auto iter4 = lst.end();
auto iter5 = lst.erase(iter1, iter4); // 删除所有元素
list<int> lst2 = { 1, 2, 3, 4 };
auto iter6 = lst2.end();
auto iter7 = iter6;
auto iter8 = lst2.erase(iter6, iter7); // iter6到iter7直接没有任何元素,返回尾后迭代器
cout << lst2.size() << endl;
system("pause");
return 0;
}
练习9.26:
#include<list>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
auto iter1 = begin(ia);
auto iter2 = end(ia);
vector<int> vec(iter1, iter2);
list<int> lst(iter1, iter2);
auto iter3 = vec.begin();
auto iter4 = lst.begin();
while (iter3 != vec.end())
{
if (*iter3 % 2)
{
iter4 = lst.erase(iter4);
++iter3;
}
else
{
iter3 = vec.erase(iter3);
++iter4;
}
}
cout << "vec:" << endl;
for (auto temp : vec)
{
cout << temp << endl;
}
cout << "lst:" << endl;
for (auto temp : lst)
{
cout << temp << endl;
}
system("pause");
return 0;
}
特殊的forward_list操作
- 在单向链表中访问一个元素的前驱并不简单,所以forward_list中删除或者添加操作都是通过改变给定元素的元素来操作的
- before_begin/cbefore_begin:返回链表首元素之前的迭代器,该迭代器无法解引用
- insert_after(p,t):在迭代器p位置之后插入元素t
- insert_after(p,n,t):在迭代器p位置之后插入n个元素t
- insert_after(p,b,e):在迭代器p位置之后插入迭代器b到e范围内的元素
- insert_after(p,{...}):在迭代器p位置之后插入花括号列表内的元素
- emplace_after(p,t):在迭代器p位置之后构造一个元素并返回指向该元素的迭代器
- erase_after(p):删除迭代器p之后的元素
- erase_after(b,e):删除迭代器b到e之间的元素
9.3.4节练习
练习9.27:
#include<list>
#include<vector>
#include<forward_list>
#include<iostream>
using namespace std;
int main()
{
forward_list<int> flst = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto prev = flst.before_begin();
auto curr = flst.begin();
while (curr != flst.end())
{
if (*curr % 2)
{
curr = flst.erase_after(prev);
}
else
{
++prev;
++curr;
}
}
for (auto temp : flst)
{
cout << temp << endl;
}
system("pause");
return 0;
}
练习9.28:
#include<list>
#include<vector>
#include<forward_list>
#include<iostream>
using namespace std;
void sortLst(forward_list<string>& flst, const string& s1, const string& s2)
{
auto curr = flst.begin();
bool inLstFlag = false;
forward_list<string>::iterator end_iter;
while (curr != flst.end())
{
if (*curr == s1)
{
curr = flst.emplace_after(curr, s2);
inLstFlag = true;
}
else
{
++curr;
}
}
if (!inLstFlag)
{
auto iter_begin = flst.begin();
while (next(iter_begin) != flst.end())
{
++iter_begin;
}
flst.emplace_after(iter_begin, s2);
}
}
int main()
{
forward_list<string> fstr = { "qwe", "asd", "zxc", "rty", "qwe"};
string str1 = "qwe1";
string str2 = "hello";
sortLst(fstr, str1, str2);
for (auto temp : fstr)
{
cout << temp << endl;;
}
system("pause");
return 0;
}
改变容器大小
- resize(n):将容器大小调整为n,容器原本大小超过n部分被删除,容器原本大小小于n,超过n部分进行值初始化
- resize(n,t):将容器大小调整为n,容器原本大小超过n部分被删除,容器原本大小小于n,超过n部分初始化为t
9.3.5节练习
练习9.29:
将vec容器大小调整值100,26~100元素的值为0。调用resize(10)后,容器大小调整为10,位置大于10的元素全部删除
练习9.30:
自定义类型的元素必须提供默认构造函数
容器操作可能会使迭代器失效
- 不要保存end返回的迭代器
9.3.6节练习
练习9.31:
list和forward_list的迭代器不支持+=操作,将+=操作改成++++iter
练习9.32:
不合法,迭代器会超出容器的范围
练习9.33:
会导致死循环
练习9.34:
遇到奇数时会死循环,一直在第一次出现奇数元素的位置插入奇数
vector对象是如何增长的
- shrink_to_fit:退回不需要的内存空间,具体是否退回要看编译器(只适用于vector、string、deque)
- capacity:查看容器当前最多能保存多少元素,当容器使用的实际容量没有超过capacity的最大值时,编译器不会重新分配内存空间
- reserve(n):分配至少能容纳n个元素的内存空间
9.4节练习
练习9.35:
size是当前已经使用的容量,capacity是当前容器最多可以使用的容量(未重新分配内存的前提下,当实际使用的容量超过capacity的容量编译器就会重新分配内存容量 )
练习9.36:
不可能,当size大于capacity时,容器就会重新分配内存
练习9.37:
array固定数组的容器大小是初始化后就固定,所以没有capacity。list链表是通过两个前后指针来连接各个元素的所以不需要预先分配内存
练习9.38:
略
练习9.39:
1、创建一个vector
2、为vector至少分配1024个元素空间
3、对vector容器输入元素
4、对vector调整容器大小
练习9.40:
384、768、1500、1572
额外的string操作
9.5.1节练习
练习9.41:
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<char> c = { 'a', 'b', 'c' };
auto begin = c.begin();
string temp;
while (begin != c.end())
{
temp += *begin;
++begin;
}
string s1(temp);
cout << s1 << endl;
system("pause");
return 0;
}
练习9.42:
使用reserve给容器分配至少100个空间再进行读取、写入操作
9.5.2节练习
练习9.43:
#include<iostream>
using namespace std;
void replaceStr(string& s, const string& oldVal, const string& newVal)
{
auto sIter = s.begin();
auto oldIter = oldVal.begin();
bool SameFlag;
cout << "oldStr:" << endl;
cout << s << endl;
while (sIter != s.end())
{
if (*sIter == *oldIter)
{
auto tempIter = sIter;
for (auto temp : oldVal)
{
if (*tempIter == temp)
{
++tempIter;
SameFlag = true;
}
else
{
SameFlag = false;
}
}
if (SameFlag)
{
sIter = s.erase(sIter, sIter + oldVal.size());
sIter = s.insert(sIter, newVal.begin(), newVal.end());
sIter += newVal.size();
}
else
{
++sIter;
}
}
else
{
++sIter;
}
}
cout << "newStr:" << endl;
cout << s << endl;
}
int main()
{
string s = "Hello World, tho, tho, hahtho";
string oldStr = "tho";
string newStr = "though";
replaceStr(s, oldStr, newStr);
system("pause");
return 0;
}
练习9.44:
#include<iostream>
using namespace std;
void replaceStr(string& s, const string& oldVal, const string& newVal)
{
auto sIndex = 0;
auto oldIndexVal = oldVal[0];
bool SameFlag;
cout << "oldStr:" << endl;
cout << s << endl;
while (sIndex < s.size())
{
if (s[sIndex] == oldIndexVal)
{
auto tempIndex = sIndex;
for (auto tempVal : oldVal)
{
if (s[tempIndex] == tempVal)
{
++tempIndex;
SameFlag = true;
}
else
{
SameFlag = false;
}
}
if (SameFlag)
{
s.replace(sIndex, oldVal.size(), newVal);
//sIter = s.erase(sIter, sIter + oldVal.size());
//sIter = s.insert(sIter, newVal.begin(), newVal.end());
sIndex += newVal.size();
}
else
{
++sIndex;
}
}
else
{
++sIndex;
}
}
cout << "newStr:" << endl;
cout << s << endl;
}
int main()
{
string s = "Hello World, tho1, tho, hahtho";
string oldStr = "tho";
string newStr = "though";
replaceStr(s, oldStr, newStr);
system("pause");
return 0;
}
练习9.45:
#include<iostream>
using namespace std;
string& replaceStr(string& name, const string& prefix, const string& suffix)
{
auto iter = name.begin();
name.insert(iter, prefix.begin(), prefix.end());
name.append(suffix);
return name;
}
int main()
{
string name = "kangkang";
string prefix = "Mr.";
string suffix = "Jr.";
name = replaceStr(name, prefix, suffix);
cout << name << endl;
system("pause");
return 0;
}
练习9.46:
#include<iostream>
using namespace std;
string& replaceStr(string& name, const string& prefix, const string& suffix)
{
name.insert(0, prefix);
int nameSize = name.size();
name.insert(nameSize, suffix);
return name;
}
int main()
{
string name = "kangkang";
string prefix = "Mr.";
string suffix = "Jr.";
name = replaceStr(name, prefix, suffix);
cout << name << endl;
system("pause");
return 0;
}
9.5.3节练习
练习9.47:
#include<iostream>
using namespace std;
int main()
{
string str = "ab2c3d7R4E6";
string findNum = "23746";
vector<int> numVec;
vector<int> alpVec;
string::size_type pos = 0;
while ((pos = str.find_first_of(findNum, pos)) != string::npos)
{
numVec.push_back(pos);
++pos;
}
pos = 0;
while ((pos = str.find_first_not_of(findNum, pos)) != string::npos)
{
alpVec.push_back(pos);
++pos;
}
cout << "Num:" << endl;;
for (auto temp : numVec)
{
cout << temp << endl;
}
cout << "Alp:" << endl;
for (auto temp : alpVec)
{
cout << temp << endl;
}
system("pause");
return 0;
}
练习9.48:
因为string的find检索不到字符串,会返回一个名为string::npos的静态成员变量,该变量初始值为-1,标准库将npos定义为size_type类型(unsigned),导致将-1赋值给一个无符号类型的变量(在64位系统上)->2^64-1=18446744073709551615,所以最后的值就为18446744073709551615
练习9.49:
略
9.5.5节练习
练习9.50:
#include<vector>
#include<iostream>
#include <string>
using namespace std;
int main()
{
vector<string> strVecI = { "123", "234", "345" };
int sum1 = 0;
for (auto temp : strVecI)
{
sum1 += stoi(temp);
}
cout << sum1 << endl;
vector<string> strVecD = { "3.14", "6.28", "9.42" };
double sum2 = 0;
for (auto temp : strVecD)
{
sum2 += stod(temp);
}
cout << sum2 << endl;
system("pause");
return 0;
}
练习9.51:
#include<iostream>
#include <string>
#include <vector>
using namespace std;
class Date
{
public:
Date() = default;
Date(string& date)
{
date = formatStr(date);
set_month(date);
set_day(date);
set_year(date);
}
void showDate()
{
cout << "year: " << this->year << endl;
cout << "month: " << this->month << endl;
cout << "day: " << this->day << endl;
}
private:
static const vector<vector<string>> const monthSet;
string& formatStr(string& str)
{
string::size_type pos = 0;
while ((pos = str.find_first_of(",", pos)) != string::npos)
{
str.replace(pos, 1, " ");
}
pos = 0;
while ((pos = str.find_first_of("/", pos)) != string::npos)
{
str.replace(pos, 1, " ");
}
return str;
}
void set_month(const string& date)
{
string::size_type monthIndex = date.find_first_of(" ");
string tempMonthStr(date, 0, monthIndex);
bool tempMonthFlag = false;
for (vector<string>::size_type row = 0; row < monthSet.size(); ++row)
{
for (vector<string>::size_type column = 0; column < monthSet[row].size(); ++column)
{
if (tempMonthStr == monthSet[row][column])
{
this->month = stoi(monthSet[0][column]);
tempMonthFlag = true;
}
}
}
if (!tempMonthFlag)
{
cout << "月份输入错误,自动设置为0" << endl;
this->month = 0;
}
}
void set_day(const string& date)
{
string::size_type monthIndex = date.find_first_of(" ") + 1;
string tempDayStr(date, monthIndex);
string::size_type dayIndex = date.find_first_of(" ");
string tempDay(tempDayStr, 0, dayIndex);
this->day = stoi(tempDay);
}
void set_year(const string& date)
{
string::size_type monthIndex = date.rfind(" ") + 1;
string tempYear(date, monthIndex);
this->year = stoi(tempYear);
}
unsigned year;
unsigned month;
unsigned day;
};
const vector<vector<string>> const Date::monthSet =
{
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept ",
"Oct", "Nov", "Dec"},
{"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"}
};
int main()
{
string s1("12/18/1990");
string s2("January 1,1900");
string s3("Jan 1 1900");
Date d1(s1);
d1.showDate();
Date d2(s2);
d2.showDate();
Date d3(s3);
d3.showDate();
system("pause");
return 0;
}
容器适配器
- 适配器是一种机制:一个容器接收一种已有的容器类型,使其的某种行为看起来像另一种容器的行为
- 适配器必须包含以下行为:删除、添加元素、访问尾元素
- 标准库中的适配器:栈适配器stack、队列适配器:queue、priority_queue
#include<iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> intStack; // 空栈
cout << intStack.size() << endl; // 0
for (size_t ix = 0; ix != 10; ++ix)
{
intStack.push(ix);
}
cout << intStack.size() << endl; // 10
while (!intStack.empty())
{
int value = intStack.top();
cout << value << endl;
intStack.pop();
}
cout << intStack.size() << endl; // 0
system("pause");
return 0;
}
9.6节练习
解析:
9.52节练:
#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<stack>
using namespace std;
string result(vector<string>& vec)
{
// vec:动态字符数组,保存了类似如下数据格式
// 1+2*5/10*100
// 1、遍历动态字符数组,遇到*、/进行运算
// 运算结束后将参与运算的字符都删除并将运算结果插入动态数组中
auto iter = vec.begin();
int retInt;
while(iter != vec.end())
{
if (*iter == "/" || *iter == "*")
{
if (*iter == "/")
{
retInt = stoi(*(iter - 1)) / stoi(*(iter + 1));
}
else
{
retInt = stoi(*(iter - 1)) * stoi(*(iter + 1));
}
iter = vec.erase(iter - 1, iter + 2);
stringstream retStr;
retStr << retInt;
string a = retStr.str();
iter = vec.insert(iter, retStr.str());
}
++iter;
}
// 2、遍历动态字符数组,遇到+、-进行运算
// 运算结束后将参与运算的字符都删除并将运算结果插入动态数组中
iter = vec.begin();
while (iter != vec.end())
{
if (*iter == "+" || *iter == "-")
{
if (*iter == "+")
{
retInt = stoi(*(iter - 1)) + stoi(*(iter + 1));
}
else
{
retInt = stoi(*(iter - 1)) - stoi(*(iter + 1));
}
iter = vec.erase(iter - 1, iter + 2);
stringstream retStr;
retStr << retInt;
string a = retStr.str();
iter = vec.insert(iter, retStr.str());
}
++iter;
}
// 3、返回运算结果
return vec[0];
}
string myOperator(stack<char>& charStack)
{
// charStack:包含一组括号内的所有字符的栈
// 数据如下格式:
// 1
// +
// 2
// *
// 5
// /
// 1
// 0
// *
// 1
// 0
// 0
// 1.1、创建一个动态字符数组用来重新将这些被分割的单个字符组合起来
vector<string> StrVec;
// 1.2、创建一个临时字符串,用来接收字符
string tempStr = "";
// 1.3、创建一个临时字符,从栈接收字符
char tempChar;
while (!charStack.empty())
{
// 1.4、从栈顶获取字符
tempChar = charStack.top();
// 1.5、如果该字符不是+、-、*、/就将这些字符拼接起来
if (tempChar != '+' && tempChar != '-' && tempChar != '*' && tempChar != '/')
{
// 1.6、需要将数据的格式从char转化成string才能进行拼接
// 通过字符串流将字符转化成字符串
stringstream tempStream;
tempStream << tempChar;
// 1.6、拼接字符,并将数据放在临时字符串中存储
tempStr.insert(tempStr.size(), tempStream.str());
}
else
{
// 1.7、进入else流程说明遇到+、-、*、/,代表一个字符串拼接完成
// 将拼接完成的字符串添加到动态字符数组中进行存储
StrVec.push_back(tempStr);
// 1.8、将+、-、*、/运算符的格式从char转化成string
stringstream tempStream;
tempStream << tempChar;
string temp = tempStream.str();
// 1.9、将+、-、*、/运算符添加到vector<string>中
StrVec.push_back(temp);
// 2.0、将临时字符串初始化
tempStr = "";
}
charStack.pop();
}
// 2.1、因为是按照遇到+、-、*、/运算符才会将临时字符串中的数据添加到
// 动态字符数组中,所以会导致最后一个运算符后面的临时字符串漏添加的情况
// 手动将最后一个临时字符串中的数据添加到动态字符数组中
StrVec.push_back(tempStr);
// 2.2、result可以将动态字符数组中数据进行计算获得结果并返回
return result(StrVec);
}
stack<char>& getExpression(const string& expStr, stack<char>& charStack)
{
// expStr:包含括号的字符串表达式
// charStack:用来处理括号的栈
// 1、遍历表达式字符串中的每一个字符
for(auto expChar: expStr)
{
// 1.1、将表达式字符串中每一个字符都压到栈中
charStack.push(expChar);
// 1.2、如果遍历过程中遇到)括号就开始删除栈顶元素直到遇到第一个相匹配的左(括号
if (expChar == ')')
{
// 1.3、括号不参与计算所以先将括号删除
charStack.pop(); // 去掉右括号)
// 1.4、声明一个临时变量(栈)用来保存一对括号内的所有字符
stack<char> tempStrStack;
// 1.5、获取表达式中括号中的最右边的字符
char tempStr = charStack.top();
// 1.6、用这个字符作为循环条件,如果该字符等于(左括号,就停止循环
// 当循环停止就代表一对括号内的字符全部被提取到临时变量(栈)中
while (tempStr != '(')
{
// 1.7、将括号内的字符串压入临时栈中
tempStrStack.push(tempStr);
// 1.8、从表达式字符串中删除已压入临时栈中的字符
charStack.pop();
// 1.9、获取新的字符用来作为下一轮循环的判断的条件
tempStr = charStack.top();
}
// 2.0、跳出循环代表当前字符=(,括号不参与计算,删除
// 跳出循环也代表此时的临时栈中已经保存了一组括号内的所有字符
charStack.pop();
// 2.1、myOperator函数可以计算该组内的字符代表的数学意义,并返回结果
string temp = myOperator(tempStrStack);
// 2.2、将结果逐字符压入栈中
for (auto ret : temp)
{
charStack.push(ret);
}
}
}
// 2.3、将处理过的表达式返回
return charStack;
}
int main()
{
// 假设一个括号表达式如下:
// 不考虑异常数据的情况比如只有一个括号:1+2)
// 不考虑除+、-、*、/、(、)、以外的运算符
string testStr("1+3*3/(1+2*5/10*100)+((1+2)*2)");
// 定义一个空栈,用来处理括号表达式
stack<char> charStack;
charStack = getExpression(testStr, charStack);
// 打印处理过的表达式,由于栈是先进后出,所以打印的数据要从右向左读
while (!charStack.empty())
{
char temp = charStack.top();
cout << temp;
charStack.pop();
}
cout << endl;
system("pause");
return 0;
}
勇敢和愚蠢只有一剑之差