C++ primer 5th 第9章 顺序容器
=====================================================================
第9章 顺序容器 292页 顺序容器概述
=====================================================================
//QQ108201645编写
表9.1: 顺序容器类型 | |
vector |
可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能变慢 |
deque |
双端队列,支持快速随机访问.在头尾位置插入/删除速度很快 |
list |
双向链表.只支持双向顺序访问.在list中任何位置进行插入/删除操作速度都很快 |
forward_list |
单向链表.只支持单向顺序访问.在链表任何位置进行插入/删除操作速度都很快 |
array |
固定大小数组.支持快速随机访问.不能添加或删除元素 |
string |
与vector相似的容器,但专门用于保存字符.随机访问快.在尾部插入/删除速度快 |
=====================================================================
#ifdef DECLARATION
•通常使用vector是最好的选择,除非你有更好的理由选择其他容器
•注重空间开销的,不要使用list或forward_list
•只在头尾,不在中间插入/删除元素的,使用deque
•在中间插入/删除元素的,使用list或forward_list
练习9.1:对于下面的程序任务,vector、deque和list哪种容器最为适合?解释
你的选择的理由。如果没有哪一种容器优于其他容器,也请解释理由。
(a) 读取固定数量的单词,将它们按字典序插入到容器中。我们将在下一章中看到,
关联容器更适合这个问题。
"按字典序插入到容器中"意味着进行插入排序操作, 从而需要在容器内部
频繁进行插入操作, vector在尾部之外的位置插入和删除元素很慢, deque在头尾之外的位置插
入和删除元素很慢, 而list在任何位置插入和删除速度都很快.因此, 这个任何选择list更为合适, 当然,
如果不是必须边读取单词边插入到容器中.可以使用vector, 将读入的单词依次追加到尾部, 读取完毕后, 调用
排序算法将单词重排为字典序.
(b) 读取未知数量的单词,总是将单词插入到末尾。删除操作在头部进行。
由于需要在头和尾分别进行插入和删除操作.因此,将vector排除在外,
deque和list都可以达到很好的性能.如果还需要频繁的进行随机访问,则deque
更好
(c) 从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出。
由于整数占用空间很小,且快速的排序算法需要频繁随机访问元素,将list排除在外,由于无须在头部进行插入和
删除操作,因此使用vector即可,无须使用deque
#endif
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
// Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
explicit Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n) {}
/*Sales_data(std::istream& is)
{
read(is, *this);
}*/
//保留默认构造函数
explicit Sales_data(std::string s = "")
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
//只允许出现在类内声明构造函数时使用explicit
explicit Sales_data(std::istream& is)
{
read(is, *this);
}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend ifstream& read(ifstream& in, Sales_data& item)
{
double price = 0;
in >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return in;
}
friend ofstream& print(ofstream& out, const Sales_data& item)
{
out << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price() << endl;
return out;
}
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::istream& read(std::istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream& print(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price() << endl;
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(std::istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include <list>
#include <deque>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
struct noDefault
{
int num;
public:
noDefault(int n)//如果使用默认构造函数只要改成noDefault(int n=0):num(n){}
:num(n){}
};
int main()
{
list<Sales_data> Sal;//保存Sales_data对象的list
deque<double> lst;//保存double的deque
cout<<sizeof(Sal)<<" "<<Sal.size()<<endl;
vector<vector<string>> lines;//vector的vector
/*此处lines是一个vector,其元素是string的vector,
较旧的编译器需要在两个尖括号之间输入空格vector<vector<string> >*/
//假定noDefault是一个没有默认构造函数的类型
int init = 0;
vector<noDefault> v1(10,init);//正确提供了元素的初始化
// vector<noDefault> v2(10);//错误,必须提供一个元素用来初始化
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 295页
=====================================================================
//QQ108201645编写
表9.2: 容器操作 | |
iterator |
此容器的迭代器类型 |
const_iterator |
可以读取元素,但不能修改元素的迭代器类型 |
size_type |
无符号整数类型, 足够保存此种类型最大可能容器的大小 |
difference_type |
带符号整数类型, 足够保存两个迭代器之间的距离 |
value_type |
元素类型 |
reference |
元素的左值类型: 与value_type&含义相同 |
构造函数 | |
C c; |
默认构造函数, 构造空容器(array, 参见第301页) |
C c1(c2); |
构造c2的拷贝c1 |
C c(b, e); |
构造c,将迭代器b和e指定的范围内的元素拷贝到c (array)不支持 |
C c{a, b, c…}; |
列表初始化 |
赋值与swap | |
c1 = c2 |
将c1中的元素替换为c2中的元素 |
c1 = {a, b, c…} |
将c1中的元素替换为列表中的元素(不适用array) |
a.swap(b) |
交换a与b的元素 |
swap(a, b) |
等价与a.swap(b) |
大小 | |
c.size() |
c中元素的数目(不支持forward_list) |
c.max_size() |
c可保存的最大元素数目 |
c.empty() |
若c中存储了元素,返回false, 否则返回true |
添加/删除元素(不适用array) 注: 在不同容器中, 这些操作的接口都不同 | |
c.insert(args) |
将args中的元素拷贝进c |
c.emplace(inits) |
使用inits构造c中的一个元素 |
c.erase(args) |
删除args指定的元素 |
c.clear() |
删除c中的所有元素, 返回void |
关系运算符 | |
==, != |
所有容器都支持相等(不等)运算符 |
<, <=, >, >= |
关系运算符(无序关联容器不支持) |
获取迭代器 | |
c.begin(), c.end() |
返回指向c的首元素和尾元素之后位置的迭代器 |
c.cbegin(), c.cend() |
返回const_iterator |
反向容器的额外成员(不支持forward_list) | |
reverse_iterator |
按逆序寻址的迭代器 |
const_reverse_iterator |
不能修改的逆序迭代器 |
c.rbegin(), c.rend() |
返回指向c的尾元素和首元素之前位置的迭代器 |
c.crbegin(), c.crend() |
返回const_reverse_iterator |
=====================================================================
第9章 顺序容器 297页 使用左闭合范围蕴含的编程假定
=====================================================================
//QQ108201645编写
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> dvec{ 1,2,3,4,5,6,7,8,9 };
deque<int>::iterator begin = dvec.begin(), end = dvec.end();
int val = 0, num = 100;
#ifdef DECLARATON
如果begin与end相等,则范围为空
如果begin与end不相等,则范围至少包含一个元素,且begin指向该范围中的第一个元素
我们可以对begin递增若干次,使得begin == end
#endif
while (begin!=end)
{//把val的值 赋值给迭代器里的内容
*begin = val;//正确: 范围非空,因此begin指向下一个元素
++begin;//移动迭代器,获取下一个元素
val += num;
}
for (auto i : dvec)
cout << i << " ";
cout<<endl;
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 297页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
/*练习9.3:构成迭代器范围的迭代器有何限制?
begin和end必须指向同一个容器,且end不在begin之前*/
/*练习9.4:编写函数,接受一对指向vector<int>的迭代器和一个int值。在两个
迭代器指定的范围中查找给定的值,返回一个布尔值来指出是否找到。*/
bool search_vec(vector<int>::iterator beg,
vector<int>::iterator end, int val)
{
for (; beg != end; ++beg)//遍历范围
if (*beg == val)//判断是否与给定值相同
return true;
return false;
}
/*练习9.5:重写上一题的函数,返回一个迭代
器指向找到的元素。注意,程序必须处理未找到给定值的情况*/
vector<int>::iterator search_vecNum(vector<int>::iterator beg,
vector<int>::iterator end, int val)
{
for (; beg != end; ++beg)//遍历范围
if (*beg == val)//判断是否与给定值相同
return beg;//搜索成功,返回元素对应迭代器
return end;//搜索失败,返回尾后迭代器
}
int search_Num(vector<int>::iterator beg,
vector<int>::iterator end, int val)
{
while (beg != end)//遍历范围
{
if (*beg == val)//判断是否与给定值相同
return *beg;//搜索成功,返回元素对应迭代器
++beg;
}
return -1;//搜索失败,返回尾后迭代器 -1
}
int main()
{
vector<int> ivec = { 1,2,3,4,5,6,7,8,9 };
cout << search_vec(ivec.begin(), ivec.end(), 3) << endl;
cout << search_vec(ivec.begin(), ivec.end(), 8) << endl;
cout << search_vecNum(ivec.begin(), ivec.end(), 3)-ivec.begin() << endl;//返回迭代器位置
cout << search_vecNum(ivec.begin(), ivec.end(), 82)-ivec.begin() << endl;
cout << search_Num(ivec.begin(), ivec.end(), 3) << endl;//返回迭代器位置的数
cout << search_Num(ivec.begin(), ivec.end(), 82) << endl;
/*练习9.6:下面程序有何错误?你应该如何修改它?
list<int> lst1;
list<int>::iterator iter1 = lst1.begin(), iter2 = lst1.end();
while (iter1 < iter2) / * ... * /
与vector和deque不同,list的迭代器不支持<运算,只支持递增,递减, == 以及!=运算
原因在于这几种数据结构实现上不同,vector和deque将元素在内存中连续保存,而list则是将元素
以链表方式存储,因此前者可以方便的实现迭代器的大小比较,(类似指针的大小比较)来体现元素的
前后关系,而在list中,两个指针的大小关系与它们指向的元素的前后关系并不一定是吻合的,实现<
运算将会非常困难和低效. */
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 298页
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
using namespace std;
int main()
{
list<string>::iterator iter;//iter是通过list定义的一个迭代器类型
vector<int>::difference_type count;//count是通过vector<int>定义的一个difference类型
list<string> slst{ "123","456","789" };
for (auto beg = slst.begin(); /*beg < slst.end();//链表没有<运算符*/
beg!=slst.end(); ++beg)
cout << *beg << " ";
cout<<endl;
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 298页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
using namespace std;
int main()
{
list<string>::iterator iter;//iter是通过list定义的一个迭代器类型
vector<int>::difference_type count;//count是通过vector<int>定义的一个difference类型
list<string> slst{ "123","456","789" };
for (auto beg = slst.begin(); /*beg < slst.end();//链表没有<运算符*/
beg!=slst.end(); ++beg)
cout << *beg << " ";
cout<<endl;
/*练习9.7:为了索引int的vector中的元素,应该使用什么类型?
vector<int>::size_type
vector<int>::iterator
练习9.8:为了读取string的list中的元素,应该使用什么类型?如果写入list,又应该使用什么类型?
list<string>::const_iterator
list<string>::iterator
为了读取string的list元素,应使用list<string>::value_type
写入数据需要非常量引用类型,因此使用list<string>::reference
*/
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 298页 begin和end成员
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
using namespace std;
int main()
{
list<string> a = { "Milton","Shakespeare","Austen" };
auto it1 = a.begin();//list<string>::iterator类型
cout<<*it1<<endl;
auto it2 = a.rbegin();//list<string>::reverse_iterator类型
//不以c开头都是被重载过的
cout << *it2 << endl;
auto it3 = a.cbegin();//list<string>::const_iterator类型
auto it4 = a.crbegin();//list<string>::const_reverse_iterator类型
//显式指定类型
list<string>::iterator it5 = a.begin();
list<string>::const_iterator it6 = a.begin();
list<string>::reverse_iterator it_1 = a.rbegin();
//是iterator还是const_iterator依赖于a的类型
auto it7 = a.begin();//仅当a是const时,it7是const_iterator
auto it8 = a.cbegin();//it8是const_iterator
/*
当auto与begin或end结合使用时,获得的迭代器类型依赖于容器类型,与我们
想要如何使用迭代器毫不相干,但以c开头的版本还是可以获得const_iterator的,
而不管容器的类型是什么
*/
#ifdef DECLARATION
练习9.9:begin和cbegin两个函数有什么不同?
cbegin是C++新标准引入的,用来与auto(reverse_iterator或const_reverse_iterator)结合使用
它返回指向容器第一个元素的const迭代器,可以用来只读的访问容器元素,但不能对容器元素进行
修改.因此当不需要写访问时,应该用cbegin
begin是被重载过的,有两个版本,const与非const版本,非const可以对容器元素进行修改
#endif
/*练习9.10:下面4个对象分别是什么类型?*/
{//局部定义
vector<int> v1;
const vector<int> v2;
#ifdef DECLARATION
auto it1 = v1.begin(), it2 = v2.begin();
#endif
auto it3 = v1.cbegin(), it4 = v2.cbegin();
/*auto只能推断同一类型,因此auto it1 = v1.begin(), it2 = v2.begin(); 是错误的
it3与it4都是c开头的cbegin带const,所以类型一样,都是vector<int>::const_iterator*/
}
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 299页 容器定义和初始化
=====================================================================
//QQ108201645编写
表9.3: 容器定义和初始化 | |
C c |
默认构造函数.如果C是一个array, 则c中元素按默认方式初始化,否则为空 |
C c1(c2) C c1=c2 |
c1初始化为c2的拷贝. c1和c2必须是相同类型(即, 它们必须是相同的容器类型,且保存的是相同的元素类型; 对于array类型,两者还必须有相同大小) |
C c{a, b, c…} C c={a, b, c…} |
c初始化为初始化列表中元素的拷贝,列表中元素的类型必须与C的元素类型相容,对于array类型,列表中元素数目必须等于或小于array的大小,任何遗漏的元素都进行值初始化(参见3.3.1节,第88页) |
C c(b, e) |
c初始化为迭代器b和e指定范围中的元素的拷贝,范围中元素的类型必须与C元素类型相容(array不适用) |
只有顺序容器(不包括array)的构造函数才能接受大小参数 | |
C seq(n) |
seq包含n个元素,这些元素进行了值初始化;此构造函数是explicit的(参见7.5.4节,第265页).(string不适用) |
C seq(n,t) |
seq包含n个初始化的值为t的元素 |
=====================================================================
第9章 顺序容器 300页 将一个容器初始化为另一个容器的拷贝
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
#include <forward_list>
using namespace std;
int main()
{//每个容器有三个元素,则给定的值进行初始化
list<string> authors = { "Milton","Shakespeare","Austen" };
vector<const char*> articles = { "a","an","the" };
list<string> list2(authors);//正确.类型匹配
//deque<string> authList(authors);//错误,容器类型不匹配
//vector<string> words(articles);//错误,容器类型不匹配
//正确:可以将const char*元素转换成为string
forward_list<string> word(articles.begin(), articles.end());
//当将一个容器初始化为另一个容器的拷贝时,两个元素的容器类型和元素类型都必须相同
//拷贝元素,直到(但不包括)it指向的元素
list<string>::iterator it = authors.end();
deque<string> authList1(authors.begin(), it);
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
#include <forward_list>
#include <array>//包含数组array头文件
using namespace std;
int main()
{//每个容器有三个元素,则给定的值进行初始化(列表初始化)
list<string> authors = { "Milton","Shakespeare","Austen" };
vector<const char*> articles = { "a","an","the" };
//与顺序容器大小相关的构造函数
vector<int> ivec(10, -1);//10个int元素,每个都初始化主动脉-1
list<string>svec(10, "hi!");//10个string元素,每个都初始化主动脉"hi!"
forward_list<int> i_f_lst(10);//10个元素,每个都初始化为0
deque<string> sque(10);//10个元素,每个都初始化为string
//标准库array具有固定大小
//与内置数组一样,标准库array的大小也是类型的一部分
//当定义一个array时,除了指定元素类型,还要指定容器大小
array<int, 42> iarr1 = { 0 };//类型为:保存42个int的数组,初始化为0
array<string, 10> sarr1;//类型为:保存10个string的数组
array<int, 10>::size_type i;//数组类型包括元素类型和大小
// array<int>::size_type j;//错误,array<int>不是一个类型
array<int, 10> ia1;//10个默认初始化的int
array<int, 10> ia2 = { 0,1,2,3,4,5,6,7,8,9 };//列表初始化
array<int, 10> ia3 = { 42 };//ia3[0]为42,剩余元素为0
int digs[10] = { 0,1,2,3,4,5,6,7,8,9 };
// int cpy[10] = digs;//错误:内置数组不支持拷贝或赋值
array<int, 10> digits = { 0,1,2,3,4,5,6,7,8,9 };
array<int, 10> copy = digits;//正确,只要数组类型匹配即合法
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
#include <forward_list>
#include <array>//包含数组array头文件
using namespace std;
int main()
{
/*练习9.11:对6种创建和初始化vector对象的方法,每一种都给出一个实例。解释每
个vector包含什么值。*/
vector<int> ivec1;//创建一个容器 名为ivec1的对象,默认初始化化
vector<int> ivec2(ivec1);//创建一个容器对象名为ivec2,把ivec1的内容拷贝给ivec2(容器类型与元素类型必须相同)
vector<int> ivec3 = { 1,2,3,4,5,6,7,8,9 };//创建一个容器名为ivec3的变量,用列表初始化
vector<int> ivec4(10, -1);//包含10个元素并初始化为-1
vector<int> ivec5(10);//包含10个被默认初始化的元素
vector<int> ivec6(ivec3.begin()+1, ivec3.end()-1);//包含从ivec3[1]到ivec[7]的元素
for (auto i : ivec6)
cout << i << " ";
cout << endl;
/*练习9.12:对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的
构造函数,解释它们的不同。
接受一个容器创建拷贝的构造函数要求容器类型和元素类型必须一致。
接受两个迭代器创建拷贝的构造函数只要求元素类型一致或者能隐式转换得到。*/
/*练习9.13:如何从一个list初始化一个vector?从一个vector
又该如何创建?编写代码验证你的答案。*/
list<int> ilst = { 1,2,3,4,5,6 };
vector<double> d_vec1(ilst.begin(), ilst.end());
vector<int> i_vec1(ilst.begin(), ilst.end());
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 302页 赋值和swap
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <string>
#include <forward_list>
#include <array>//包含数组array头文件
using namespace std;
int main()
{
string a = "abc", b = "def", c = "ghi";
array<string,3> c1 = { "123","456","789" };
for (auto sarr : c1)
{
cout << sarr << " ";
}
cout<<endl;
array<string, 3> c2 = c1;//将c1的内容赋值给c2
c1 = { a,b,c };//赋值后,c1的大小为3
for (auto sarr : c1)
{
cout << sarr << " ";
}
cout << endl;
array<int, 10> a1 = { 0,1,2,3,4,5,6,7,8,9 };
array<int, 10>a2 = { 0 };//所有元素为0
a1 = a2;//a1的10个元素替换成a2的十个元素
a1 = { 5 };//错误.不能将一个花括号列表赋值给数组,其实是把a1[0]的值改为5
system("pause");
return 0;
}
=====================================================================
#include <array>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
void Display(array<int,10>& iarr)//输出元素到控制台
{
for (auto i : iarr)
cout << i << " ";
cout<<endl;
}
int main()
{
array<int, 10> iarr{ 1,2,3,4,5,6,7,8,9,10 };
for (int &i : iarr)
{
for (int &j : iarr)
if (i > j)//从大到小冒泡排序
{
i ^= j ^= i ^= j;
}
}
Display(iarr);
for (array<int, 10>::iterator it = iarr.begin();
it != iarr.end();++it)
if (*it == 5)//把元素中的5改成0
*it = 0;
Display(iarr);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 302页
=====================================================================
//QQ108201645编写
表9.4: 容器赋值运算 | |
c1=c2 |
将c1中的元素替换成c2中的元素,c1与c2具有相同类型 |
c={a, b, c…} |
将c1中元素替换成初始化列表中元素的拷贝(array不适用) |
swap(c1,c2) |
交换c1和c2的元素.c1和c2必须具有相同的类型,swap通常比从c2向c1拷贝元素快的多 |
assign操作不适用关联容器与array | |
seq.assign(b,e) |
将seq中的元素替换成迭代器b和e所表示的范围中的元素.迭代器b和e不能指向seq中的元素 |
seq.assign(il) |
将seq中的元素替换成初始化列表il中的元素 |
seq.assign(n,t) |
将seq中的元素替换成n个值为t的元素 |
赋值相关运算符会导致指向左边容器内部的迭代器,引用和指针失效.而swap操作将容器内容交换不会导致指向容器的迭代器,引用和指针失效(容器类型是array和string的情况除外) |
=====================================================================
第9章 顺序容器 303页 使用assign(仅顺序容器)
=====================================================================
//QQ108201645编写
#include <array>
#include <vector>
#include <iostream>
#include <list>
using namespace std;
int main()
{
string buf="123";
buf.insert(1, "xyz");//从第一位开始插入"xyz"字符串
/*赋值运算符assign(仅顺序容器)
将右边运算对象中所有元素拷贝到左边运算对象中.array除外*/
list<string> names;
vector<const char*> oldstyle = { "abc", "def" };
// names = oldstyle;//错误:容器类型不一样
//正确:可以将const char*转换成string
names.assign(oldstyle.cbegin(), oldstyle.cend());//替换为迭代器指定的范围中的元素的拷贝
//assign的参数决定了容器中将有多少个元素以及它们的值是什么
//等价于slist1.clear();
//后跟slist1.insert(slist1.begin(),10,"Hiya!");
list<string> slist1(1);//1个元素,元素内容是一个空的string
slist1.assign(10, "Hiya!");//10个元素,每个都是"Hiya!"
slist1.insert(slist1.begin(), 5, "abc");//从benin()开始的位置插入5个元素,并且内容为"abc"字符串
//使用swap
vector<string> svec1(10,"abc");//10个元素的vector
vector<string> svec2(24,"def");//24个元素的vector
swap(svec1, svec2);//必须是相同类型,交换svec1与svec2的结构与内容
svec1.swap(svec2);//同上
array<int, 10> iarr1{ 0 };
//array<int, 20> iarr2;//错误,数组元素个数不同,不能交换
array<int, 10> iarr2{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };
iarr1.swap(iarr2);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 304页 练习题
=====================================================================
//QQ108201645编写
#include <vector>
#include <iostream>
#include <list>
#include <string>
using namespace std;
int main()
{
/*练习9.14:编写程序,将一个list中的char *指针(指向C风格字符串)元素赋值
给一个vector中的string。*/
list<const char *> clst1 = { "hello","world","!" };
vector<string> svec1;
//容器类型不同,不能直接赋值
//svec1=clst1;
svec1.assign(clst1.begin(), clst1.end());//元素类型相同,可以用范围赋值
cout<<svec1.capacity()<<" "<<svec1.size()<<" "//capacity()返回元素的总数,容量
<<svec1[0]<<" "<<svec1[svec1.size()-1]<<endl;
for (size_t i = 0; i < 5; i++)
{
svec1.push_back("abc");
cout << svec1.size() << " " << svec1.capacity() << " " << svec1.max_size() << endl;
}//容器是线性表,内存空间是连续的,每次添加一个新元素,系统可能会自动分配额外的存储空间。
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 304页 容器大小的操作
=====================================================================
#ifdef DECLARATION
如果两个容器具有相同大小且所有元素都对应相等,则这两个容器相等,否则
两个容器不等
如果两个容器大小不同,但较小容器中每个元素都等于较大容器中的对应元素,则
较小容器小于较大容器
如果两个容器都不是另一个容器的前缀子序列,则它们的比较结果取决于第一个
不相等的元素的比较结果
#endif
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> v1 = { 1,3,5,7,9,12 };
vector<int> v2 = { 1,3,9 };
vector<int> v3 = { 1,3,5,7 };
vector<int> v4 = { 1,3,5,7,9,12 };
cout << (v1 < v2) << endl;//true;v1和v2在元素[2]处不同;v1[2]小于等于v2[2]
cout << (v1 < v3) << endl;//false;//所有元素都有相等,但v3中元素数目更少
cout << (v1 == v4) << endl;//true ;每个元素都相等,且v1和v4大小相同
cout << (v1 == v2) << endl;//false;v2元素数目比v1少
cout << (v1 == v3) << endl;//false;v3元素数目比v1少
/*
第7章定义的Sales_data类型并没定义==和<运算,因此不能比较两个保存Sales_data元素的容器
vector<Sales_data> storeA,storeB;
if(storeA<storeB)//错误;Sales_data没有<运算符
*/
/*练习9.15:编写程序,判定两个vector<int>是否相等。*/
vector<int> ivec{ 0,1,2,3,4,5,6 };
vector<int> ivec1{ 0,1,2,3 };
if (ivec==ivec1)
cout<<"ivec == ivec1 ok"<<endl;
else if (ivec<ivec1)
cout << "ivec < ivec1 ok" << endl;
else
cout << "ivec > ivec1 ok" << endl;
/*练习9.16:重写上一题的程序,比较一个list中的元素和一个vector
中的元素。*/
list<int> ilist{ 0,1,2,3,4,5,6 };
vector<int> ivec2{ 0,1,2,3 };
vector<int> ivec3{ 0,1,2,3,4,5,6 };
bool v_equal(vector<int>& ivec, list<int>& ilist);//函数声明
//函数声明可以放在int main()上面或main中,这里方便看才声明在这,在后置调用函数中才必须声明
cout << v_equal(ivec2, ilist) << endl;
cout << v_equal(ivec3, ilist) << endl;
vector<int> ivec4{ 0,1,2,3,4,5,8 };
bool v_equal( list<int>& ilist, vector<int>& ivec);//函数声明(第二种写法)
cout << v_equal(ilist,ivec4) << endl;//根据形参类型位置不同调用重载函数
/*
练习9.17:假定c1和c2是两个容器,下面的比较操作有何限制(如果有的话)?
if (c1 < c2)
首先,容器类型必须相同,元素类型也必须相同
其次,元素类型必须支持<运算符
*/
system("pause");
return 0;
}
bool v_equal(vector<int>& ivec, list<int>& ilist)
{
if (ivec.size() != ilist.size())//比较大小是否一样
return false;//不同返回0
auto ilstbeg = ilist.cbegin(), ilstend = ilist.cend();
for (auto i : ivec)
{
if (i != *ilstbeg++&&ilstbeg != ilstend)//如果两者不同(同时迭代器++),并且迭代器还未结束,则返回false,
return false;
}
return true;
}
bool v_equal(list<int>& ilist, vector<int>& ivec)
{
if (ivec.size() != ilist.size())//比较大小是否一样
return false;//不同返回0
list<int>::const_iterator ilstbeg = ilist.cbegin(), ilstend = ilist.cend();
for (vector<int>::const_iterator it=ivec.cbegin();
it!=ivec.cend()&&ilstbeg!=ilstend;//如果两者迭代器都未结束,则运行循环操作
++it,++ilstbeg)//自增++ 移动一位
{
if (*it != *ilstbeg)
return false;
}
return true;
}
=====================================================================
第9章 顺序容器 305页 顺序容器操作
=====================================================================
表9.5: 向顺序容器添加元素的操作 | |
这此操作会改变容器的大小; array不支持这些操作 | |
forward_list有自己专有版本的insert和emplace;参见9.34节(第312页) | |
forward_list不支持push_back和emplace_back | |
vector和string不支持push_front和emplace_front | |
c.push_back(t) |
在c的尾部创建一个值为t或由args创建的元素,返回void |
c.emplace_back(args) | |
c.push_front(t) |
在c的头部创建一个值为t或由args创建的元素,返回void |
c.emplace_front(args) | |
c.insert(p,t) |
在迭代器p指向的元素之前创建一个值为t或由args创建的元素.返回新添加的迭代器 |
c.emplace(p,args) | |
c.insert(p,n,t) |
在迭代器p指向的元素之前插入n个值为t的元素.返回指向新添加的第一个元素的迭代器;若n为0,则返回p |
c.insert(p,b,e) |
将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前.b和e不能指向c中的元素.返回指向新添加的第一个元素的迭代器;若范围为空,则返回p |
c.insert(p,il) |
il是一个花括号包围的元素值列表.将这些给定值插入到迭代器p指向的元素之前.返回指向新添加的第一个元素的迭代器;若列表为空,则返回p |
=====================================================================
第9章 顺序容器 306页 使用push_back
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <forward_list>
#include <deque>
using namespace std;
int main()
{
vector<string> container;
string word;
cout<<"输入字符串,如果输入end,表示结束"<<endl;
while (cin>>word&&word!="end")//从标准输入读取数据,如果输入的是"end"字符串就结束
{//将单词放到容器尾部
container.push_back(word);
//将一个元素追加到一个vector的尾部,除array和forward_list外,每个顺序容器(包括string)都支持
cout << "输入字符串,如果输入end,表示结束" << endl;
}
for (auto i : container)
cout << i << " ";//依次输出迭代器的内容
cout<<endl;
word.clear();//清除内容
void pluralize(size_t cnt, string& word);//函数声明
pluralize(5,word);
for (auto i : word)
{
cout << i << " ";
}
cout<<endl;
//使用push_front
list<int> ilst1{ 1,2,3 };
//将元素添加到ilst1开头
for (size_t ix = 5; ix != 10; ++ix)
ilst1.push_front(ix);
for (auto i : ilst1)
cout << i << " ";
cout<<endl;
/*
输出:
输入字符串,如果输入end,表示结束
hello
输入字符串,如果输入end,表示结束
world
输入字符串,如果输入end,表示结束
end
hello world
s s s s
9 8 7 6 5 1 2 3
*/
/*ilst1.push_front(ix);按顺序是先插入5,放在开头,
再插入6放在开头,依次类推.所以是9 8 7 6 5形成逆序*/
deque<int> ique1;
ique1.push_front(5);//deque支持push_front
//deque在首尾之外的位置插入元素很耗时
forward_list<int> iwlist1;
iwlist1.push_front(6);//forward_list支持push_front
/*
vector,deque,list和string都支持insert成员,forward_list提供特殊版本的insert(9.3.4节,312页)
*/
system("pause");
return 0;
}
void pluralize(size_t cnt, string& word)
{
while(cnt > 1)
{//由于string是一个容器,使用push_back在string末尾添加字符
word.push_back('s');//等价于word+='s'
cnt--;
}
}
=====================================================================
第9章 顺序容器 307页 在容器中的特定位置添加元素
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <forward_list>
#include <deque>
#include <algorithm>//remove所在的文件头
using namespace std;
void Display(list<string>& v)
{
for (auto i : v)
{
cout << i << " ";
}
cout << endl;
}
void Display(vector<string>& v)
{
for (auto i : v)
{
cout << i << " ";
}
cout<<endl;
}
int main()
{
list<string> ilst{ 1," world\n" };
list<string>::const_iterator iter = ilst.begin();
ilst.insert(iter, "Hello");//将 "Hello" 添加到iter之前的位置
Display(ilst);//输出结果
vector<string> svec;
list<string> slist;
//等价于slist.push_front("Hello!");
slist.insert(slist.begin(), "Hello!");
//vector不支持push_front,但我们可以插入begin()之前
//警告!插入到vector末尾之外的任何位置都可能很慢
svec.insert(svec.begin(), "Hello");
//将元素插入到vector,deque,和string中的任何位置都是合法的,但很耗时
string buf;
buf.insert(0, " world");//只有string才能使用直接一个数传入
forward_list<int> iflst;
iflst.push_front(5);
iflst.insert_after(iflst.begin(), 6);
for (auto i : iflst)
cout << i << " ";
cout<<endl;
//插入范围内元素
svec.insert(svec.end(), 10, "Anna");//将10个元素插入到svec的末尾,将初始化为string "Anna"
Display(svec);//输出
list<string> slist1{ "abc", "def" };
vector<string> v = { "quasi","simba","frollo","scar" };
//将v的最后两个元素添加到slist1的开始位置
slist1.insert(slist1.begin(), v.end() - 2, v.end());
Display(slist1);
slist1.insert(slist1.end(), { "these","words","will","go","at","the","end" });
Display(slist1);
slist1.insert(slist1.begin(), slist1.begin(), slist1.end());
Display(slist1);//输出
//使用insert的返回值
string word;
list<string> lst;
auto iter1 = lst.begin();
cout<<"输入字符串,如果输入end则结束"<<endl;
while (cin>>word&&word!="end")
{
iter1 = lst.insert(iter1, word);
}
auto it = svec.begin();//vector<int>
it = svec.insert(it, word);
Display(lst);
Display(svec);
void DelVec(vector<string>& v,const char* s);//函数声明
//删除元素方法1
svec.erase(remove(svec.begin(), svec.end(),"Hello"), svec.end());
Display(svec);
//删除元素方法2
DelVec(svec, "Anna");//调用删除元素,删除内容是"Anna"的字符串
Display(svec);
system("pause");
return 0;
}
void DelVec(vector<string>& v, const char* s)
{
for (vector<string>::iterator it=v.begin();
it!=v.end();)
{
if (*it == s)
{
cout << "位置:" << (it - v.begin()) << " 删除 " << *it << " 成功" << endl;
it = v.erase(it);//删除当前元素,并把当前指向下一个元素
}
else
++it;//否则自增操作
}
}
=====================================================================
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <forward_list>
#include <deque>
using namespace std;
void Display(vector<string>& v)
{
for (auto i : v)
{
cout << i << " ";
}
cout << endl << endl;
}
void Display(deque<string>& v)
{
for (auto i : v)
{
cout << i << " ";
}
cout << endl << endl;
}
void Display(list<string>& v)
{
for (auto i : v)
{
cout << i << " ";
}
cout << endl << endl;
}
void Display(forward_list<string>& v)//重载函数
{
for (auto i : v)
{
cout << i << " ";
}
cout << endl<<endl;
}
int main()
{
vector<string> v1{ "hello"," world" };
deque<string> v2{ "hello1"," world1" };
list<string> v3{ "hello2"," world2" };
forward_list<string> v4{ "hello3"," world3" };
cout<<"void Display(vector<string>& v);"<<endl;
v1.insert(v1.begin(), v2.begin(), v2.end());//在v1.begin()之前插入v2.begin()到v2.end()之间的元素
v1.insert(v1.begin(), { "abc","def" });//在v1.begin()之前插入 { "abc","def" }两个元素
Display(v1);
cout << "void Display(deque<string>& v);" << endl;
v2.insert(v2.begin(), v1.begin(), v1.end()); //在v2.begin()之前插入v1.begin()到v1.end()之间的元素
v2.insert(v2.begin(), { "hij","klm" });//在v2.begin()之前插入 { "hij","klm" }两个元素
Display(v2);
cout << "void Display(list<string>& v);" << endl;
v3.insert(v3.begin(), v4.begin(), v4.end()); //在v3.begin()之前插入v4.begin()到v4.end()之间的元素
v3.insert(v3.end(), { "123","456" });//在v3.begin()之前插入{ "123","456" }两个元素
Display(v3);
cout << "void Display(forward_list<string>& v);" << endl;
vector<string> v5{ "xyz","666" };
v4.insert_after(v4.begin(), v5.begin(), v5.end());//在v4.begin()之后插入v5.begin()到v5.end()之间的元素
v4.insert_after(v4.begin(), { "whlie","do" });//在v4.begin()之后插入{ "whlie","do" }两个元素
Display(v4);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 308页 使用emplace操作
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
// Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
explicit Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n) {}
/*Sales_data(std::istream& is)
{
read(is, *this);
}*/
//保留默认构造函数
explicit Sales_data(std::string s = "")
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
//只允许出现在类内声明构造函数时使用explicit
explicit Sales_data(std::istream& is)
{
read(is, *this);
}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend ifstream& read(ifstream& in, Sales_data& item)
{
double price = 0;
in >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return in;
}
friend ofstream& print(ofstream& out, const Sales_data& item)
{
out << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price() << endl;
return out;
}
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::istream& read(std::istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream& print(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price() << endl;
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(std::istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}
#endif
―――――――――――――――――――――――――――――――――――――――
//QQ108201645编写
#include "Sales_data.h"
#include <iostream>
#include <list>
#include <vector>
using namespace std;
//typedef list<Sales_data> SLST;
typedef vector<Sales_data> SLST;//vs2017未知原因使用c.emplace(iter,"978-0590353403")报错(已找到原因,迭代器必须在c.emplace(iter,"978-0590353403"代码前),g++正常
typedef vector<int> IVEC;
void Display(SLST& v)
{
cout << "――――――――――――――――" << endl;
for (auto i : v)
cout << i << endl;
}
int main()
{
/*
新标准引入三个成员-- emplace_front,emplace和emplace_back,这些
操作构造函数而不是拷贝元素分别对应push_front,insert和push_back,
允许我们将元素放在容器头部,一个指定位置之前或容器尾部
*/
//在c的末尾构造一个Sales_data对象
//使用三个参数的Sales_data构造函数
SLST c;
c.emplace_back("978-0590353403", 25, 15.99); //(顺序创建1)
/*调用 explicit Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n) {}
*///把对象放到vector
//c.push_back("978-0590353403", 25, 15.99);//错误,没有接受三个参数的push_back版本
c.push_back(Sales_data("978-0591353408", 25, 15.99));//正确,创建一个临时的Sales_data对象传递给push_back(顺序创建2)
/*
emplace_back与push_back都会创建新的对象,不同的是emplace_back会在容器的管理窨直接创建对象.
push_back则只会创建一个局部临时对象,并将其压入容器中
*/
Display(c);
//emplace函数的参数根据元素类型而变化,参数必须与元素类型的构造函数相匹配
//iter指向c中的一个元素,其中保存了Sales_data的元素
/*如果使用vector<Sales_data>容器要把SLST::const_iterator iter = c.begin();写在
c.emplace(iter, "978-0590353403");的前面
否则执行到下面c.emplace(iter, "978-0590353403")会报错,而g++正常*/
c.emplace_back();//使用Sales_data的默认构造函数(顺序创建3)
SLST::const_iterator iter = c.begin();
c.emplace(iter, "978-0590353403");//使用Sales_data(string)(指定位置创建1,前面三个由1。2。3变成2。3。4)
Display(c);
//使用Sales_data的接受一个ISBN,一个count和一个price的构造函数
c.emplace_back("978-0591123456", 25, 15.98);
Display(c);
IVEC ivec{ 1,2,3 };
IVEC::const_iterator it = ivec.begin() + 1;
ivec.emplace(it, 333);
for (auto i : ivec)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <string>
#include <list>
#include <vector>
using namespace std;
class Sales_data
{
public:
Sales_data(const string& buf)
:buf_(buf), num_(0) {}
Sales_data(const string &buf, double num)
:buf_(buf),num_(num){}
friend ostream& operator<<(ostream& os,const Sales_data& other)
{
os << other.buf_<<" "<<other.num_;
return os;
}
private:
double num_ = 0;
string buf_;
};
void Display(vector<Sales_data>& v)
{
cout << "――――――――――――――――" << endl;
for (auto i : v)
cout << i << endl;
}
int main()
{
vector<Sales_data> c;
c.push_back(Sales_data("123",5.5) );
c.emplace_back("abc", 5.6);
vector<Sales_data>::const_iterator it = c.cbegin();
c.emplace(it, "456");
Display(c);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 309页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <deque>
#include <vector>
using namespace std;
int main()
{
/*练习9.18:编写程序,从标准输入读取string序列,存入一个deque中。
编写一个循环,用迭代器打印deque中的元素。*/
string buf;
deque<string> Sque;
cout<<"输入字符串,以输入end结束"<<endl;
while (cin>>buf&&buf!="end")//以输入end结束
{
Sque.push_back(buf);
}
Sque.insert(Sque.begin(),{"xxxx","yyyy","zzzz"});//在开始位置插入三个元素
Sque.emplace_back("hello");//创建一个末尾元素
Sque.emplace_back();//创建一个末尾空元素
Sque.emplace_front("good");//开头创建插入一个元素
deque<string>::iterator iSqueIt = Sque.begin() + 2;//当创建改变后,迭代器的获取在当前位置
Sque.emplace(iSqueIt, "go to");//指向第二个位置,创建一个string元素
for (deque<string>::const_iterator it = Sque.begin();
it != Sque.end(); ++it)
{
cout << *it << " ";
if (it==Sque.end()-1)
cout<<endl;
}
/*练习9.19:重写上题的程序,用list替代deque。列出程序要做出哪些改变。*/
//list 与deque在任何位置添加新元素都有很好的性能,遍历操作也能高效完成,因此程序与上一题并无太大差异
cout << "list方式输出" << endl;
list<string> slst;
slst.assign(Sque.begin(), Sque.end());
for (list<string>::const_iterator it = slst.begin();
it != slst.end(); ++it)
{
cout << *it << " ";
}
cout<<endl;
cout << "输入字符串,以输入end结束" << endl;
while (cin >> buf && buf != "end")
{
slst.push_front(buf);//在头部插入
}
cout << "输入字符串,以输入end结束" << endl;
while (cin >> buf && buf != "end")
{
slst.push_front(buf);//在头部插入
}
for (list<string>::const_iterator it = slst.begin();
it != slst.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <deque>
#include <vector>
using namespace std;
void ShowDequeInt(deque<int>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
/*练习9.20:编写程序,从一个list<int>拷贝元素到两个deque中。值为偶数的所
有元素都拷贝到一个deque中,而奇数值元素都拷贝到另一个deque中。*/
list<int> ilst1{ 1,2,3,4,5,6,7,8,9,10,11,12 };
deque<int>ideque1, ideque2;
list<int>::const_iterator it;
for (it = ilst1.begin(); it != ilst1.end(); ++it)
{
#ifdef DECLARATION
//方法1
//能被1整除且不能被2整除的为奇数,否则为偶数,如果只用其中一个,则不确定,
if (*it % 1 == 0 && * it % 2 != 0)
ideque1.push_back(*it);
else
ideque2.push_back(*it);
#endif
//方法2;相同置1
if (*it & 1)//与操作,见0出0全1出1,最低位是1则执行ideque1.push_back(*it);1:奇数,0:偶数
ideque1.push_back(*it);
else
ideque2.push_back(*it);
}
cout << "奇数值有:"; ShowDequeInt(ideque1);//输出ideque1的内容
cout << "偶数值有:"; ShowDequeInt(ideque2);//输出ideque2的内容
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
void ShowListStr(list<string>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
void ShowVecStr(vector<string>& v)
{
auto rbeg = v.rbegin(), rend = v.rend();//逆向,从尾元素开始遍历
while(rbeg != rend)
cout << *rbeg++ << " ";
cout << endl;
}
int main()
{
/*练习9.21:如果我们将第308页中使用insert返回值将元素添加到list中的循环
程序改写为将元素插入到vector中,分析循环将如何工作。*/
string word;
#ifdef DECLARATION
list<string> slst;
auto iter = slst.begin();
while (cin>>word&&word!="end")
{
//如果是lst.insert(iter, word);则从开头位置开始顺序插入
iter = slst.insert(iter, word);//插入位置都指向lst.begin();等价于push_front
}
ShowListStr(slst);
#endif
vector<string> ivec;
auto it = ivec.begin();
while (cin >> word && word != "end")//输入abc edf ghi 则里面内容是ghi def abc
{
//如果是ivec.insert(it, word);则从开头位置开始顺序插入
it = ivec.insert(it, word);//插入位置都指向ivec.begin();等价于push_front
}
ShowVecStr(ivec);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
void ShowVecInt(vector<int>& v)
{
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
void ShowReverseVecInt(vector<int>& v)
{
auto rbeg = v.rbegin(), rend = v.rend();//逆向,从尾元素开始遍历
while(rbeg != rend)
cout << *rbeg++ << " ";
cout << endl;
}
int main()
{
/*练习9.22:假定iv是一个int的vector,下面的程序存在什么错误?你将如何修改?*/
/*
vector<int>::iterator iter = iv.begin(),
mid = iv.begin() + iv.size() / 2;
while (iter != mid)
{
if (*iter == some_val)
iv.insert(iter, 2 * some_val);
}
*/
#ifdef DECLARATION
循环中未对iter进行递增,iter无法向中间推进,其次,即使加入了
iter++语句,由于向iv插入元素后,while中的iter已经失效,iter++
也不能起到将迭代器向前推进一个元素的作用。修改方法如下
首先,将insert返回的迭代器赋予iter,iter将指向新插入的元素y
insert将y插入到iter原来指向的元素x之前的位置,因此,需要进行两次
iter++才能将iter推进到x之后的位置。
其次insert也会使mid失效,因此,正确设置iter仍不能令循环在正确的时候
结束,还需要设置mid使之指向iv原来的中央元素,在未插入任何元素之前,位置
是iv.begin()+iv.size()/2,我们将此时iv.size()
的值记录在变量org_size中,然后在循环过程中统计新插入的元素的个数new_ele,则在任何时候,
iv.begin()+org_size/2+new_ele都能指向正确iv原来的中央元素
#endif
vector<int> iv{ 1,2,3,4,5,6 };
int some_val = 3;
vector<int>::iterator iter = iv.begin();
int org_size = iv.size(), new_ele = 0;
while (iter != (iv.begin()+org_size/2+new_ele))
{
if (*iter == some_val)
{
iter = iv.insert(iter, 2 * some_val);//插入新元素
new_ele++;
iter++,iter++;//将iter自增2到旧元素的下一个位置
}
else
++iter;
}
ShowReverseVecInt(iv);
vector<int> ivec = { 11,22,33,44,55,66,77 };
int some_v = 22;
vector<int>::iterator it = ivec.begin();
int o_size = ivec.size(), i = 0;
while (i <= o_size / 2)
{
if (*it == some_v)
{
it = ivec.insert(it, 2 * some_v);
it++;
it++;
}
else
it++;
i++;
}
ShowVecInt(ivec);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 309页 访问元素
=====================================================================
//QQ108201645编写
#include <iostream>
#include <array>
#include <string>
#include <vector>
using namespace std;
void ShowArrInt(array<int,3>& v)
{
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
array<int,3> iarr{ 10,20,30 };
/*包括array在内的每个顺序容器都有front成员,除forward_list之外的所有顺序
容器都有一个back成员函数,这两个分别是返回首元素,和尾元素的引用*/
if (!iarr.empty())
{
//val和val2是iarr中第一个元素的拷贝
auto val = *iarr.begin(), val2 = iarr.front();//返回首元素
cout<< "val = "<<val<<" val2 = "<<val2<<endl;
//val3与val4是iarr中最后一个元素值的拷贝
auto last = iarr.end();//last是尾迭代器(最后一个元素的下一个位置)
//cout << "*last = " << *last<< endl;//尾迭代器的值是无法输出
auto val3 = *(--last);//不能递减forward_list迭代器(尾迭代器自减后的位置的元素赋值给val3)
cout << "val3 = " << val3 << endl;//输出val3
auto val4 = iarr.back();//forward_list不支持
cout << "val4 = " << val4 << endl;
}
ShowArrInt(iarr);
/*此程序用两种不同方式来获取iarr中的首元素和尾元素的引用.直接方法是调用front
和back,而间接的方法就是通过begin返回的迭代器来获得首元素的引用,以及通过递减
然后解引用end返回的迭代器来获得尾元素的引用
值的注意的是:迭代器end指向的是容器尾元素之后的(不存在的)元素,为了获取尾元素,
必须首先递减这个迭代器.另一个重要之处就是,在调用front和back(或解引用begin和
end返回的迭代器)之前,要确保iarr非空,如果容器为空, if中操作的行为将是未定义的*/
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 310页 练习题
=====================================================================
//QQ108201645编写
表9.6: 在顺序容器中访问元素的操作 | |
at和下标操作只适用于string、vector、deque、和array。 back不适用于forward_list | |
c.back() |
返回c尾元素的引用。若c为空,函数行为未定义 |
c.front() |
返回c中首元素的引用。若c为空,函数行为未定义 |
c[n] |
返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c,size(),则函数行为未定义 |
c.at(n) |
返回下标为n的元素的引用,如果下标越界,则抛出一out_of_range异常 |
WARNING: 对一个空容器调用front和back,就像使用一个越界的下标一样,是一种严重的程序设计错误 |
=====================================================================
第9章 顺序容器 310页
=====================================================================
//QQ108201645编写
#include <iostream>
#include <array>
#include <string>
#include <vector>
using namespace std;
void ShowArrInt(array<int,3>& v)
{
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
//访问成员函数返回的是引用
array<int,3> c{ 10,20,30 };
if (!c.empty())
{
c.front() = 42;//将42赋值给c中的第一个元素
auto& v = c.back();//获得指向最后一个元素的引用
v = 1024;//改变c中的元素
auto v2 = c.back();//v2不是一个引用/它是c.back()的一个拷贝
v2 = 0;//未改变c中的元素
c.back() = 6; //改变c中的元素
}
ShowArrInt(c);
vector<string> svec;
//cout << svec[0];//运行时错误:vector中没有元素
cout << svec.at(0);//抛出一个out_of_range异常
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 311页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <array>
#include <string>
#include <vector>
using namespace std;
void ShowVecInt(vector<int>& v)
{
cout << "输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
/*练习9.23:在本节第一个程序(第309页)中,若c.size()为1,则val、val2、val3
和val4的值会是什么?
它们的值都将是第一个元素值。*/
/*练习9.24:编写程序,分别使用at、下标运算符、front和begin提取一个vector
中的第一个元素。在一个空vector上测试你的程序。*/
vector<int> ivec{ 10,20,30,40,50,60,70 };
ShowVecInt(ivec);
if (!ivec.empty())
{
cout<<ivec.at(2)<<endl;
ivec[3] = 3;
cout<<ivec[3]<<endl;
ivec.front() = 1;
cout<<ivec.front()<<endl;
ivec.back() = 7;
cout << ivec.back() << endl;
auto beg = ivec.begin() + 4;
*beg = 4;
cout<<*beg<<endl;
cout<<*(ivec.begin()+6)<<endl;
}
ShowVecInt(ivec);
#ifdef DECLARATION
cout<<ivec.at(9)<<endl;//如果没有这个元素就抛出一个out_of_range异常
#endif
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 311页
=====================================================================
//QQ108201645编写
表9.7: 顺序容器的删除操作 | |
这些操作会改变容器的大小, 所以不适用于array | |
forward_list有特殊版本的erase,参见9.3.4节(第312页) | |
forward_list不支持pop_back; vector和string不支持pop_front | |
c.pop_back |
删除c中尾元素. 若c为空,则函数行为未定义.函数返回void |
c.pop_front |
删除c中首元素. 若c为空,则函数行为未定义.函数返回void |
c.erase(p) |
删除迭代器p所指定的元素,返回一个指向被删除元素之后元素的迭代器,若p指向尾元素,则返回尾后(off-the-end)迭代器. 若p是尾后迭代器, 则函数行为未定义 |
c.erase(b,e) |
删除迭代器b和e所指定范围内的元素. 返回一个指向最后一个被删除元素之后的迭代器, 若e本身就是尾后迭代器,则函数也返回尾后迭代器 |
c.clear() |
删除c中的所有元素. 返回void |
WARNING: 删除deque中除首尾位置之外的任何元素都会使所有迭代器、引用和指针失效。指向vector或string中删除点之后位置的迭代器、引用和指针都会失效。 |
=====================================================================
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
void process(string& buf)
{
buf.clear();
}
void ShowListStr(list<string>& v)
{
cout << "list<string>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
void ShowListInt(list<int>& v)
{
cout << "list<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
void ShowVecInt(vector<int>& v)
{
cout << "vector<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
list<string> ilist{ "abc","def","xyz","good" };
/*auto v = ilist.front();
cout<<typeid(v).name()<<endl;*/
if (!ilist.empty())
{
process(ilist.front());//对ilist的首元素进行一些处理
ilist.pop_front();//完成处理后删除首元素
}
ShowListStr(ilist);//输出内容: list<string>& v 输出:def xyz good
//从容器内部删除一个元素
list<int> lst = { 0,1,2,3,4,5,6,7,8,9};
auto it = lst.begin();
while (it!=lst.end())
{
if (*it % 2)//若元素为奇数
it = lst.erase(it);//删除此元素
else
++it;
}
/*每个循环中检查当前是不是奇数,如果是,就删除,并将it设置为
我们所删除的元素之后的元素。如果*it为偶数,我们将it递增,从而
在下一步循环检查下一个元素*/
ShowListInt(lst);//输出内容: list<int>& v 输出:0 2 4 6 8
vector<int> ivec(lst.begin(), lst.end());
//删除多个元素
//删除两个迭代器表示的范围内的元素
//返回指向最后一个被删除元素之后位置的迭代器
it = lst.erase(lst.begin(), lst.end());//调用后it指向lst.end的位置it==lst.end()
ShowVecInt(ivec);//输出内容: vector<int>& v 输出:0 2 4 6 8
auto ivIt = ivec.begin();
ivIt = ivec.erase(ivec.begin()+1, ivec.begin() + 3);//list没有+,-,<运算
ShowVecInt(ivec);//输出内容: vector<int>& v 输出:0 6 8
//为了删除容器中的所有元素,可以调用clear,也可以用begin和end获得
//的迭代器作为参数调用erase: ivec.clear();//删除容器中所有内容
ShowListStr(ilist); //输出内容: list<string>& v 输出:def xyz good
ilist.clear();
ilist.erase(ilist.begin(), ilist.end());//等价于ilist.clear()
ShowListStr(ilist);// 输出内容: list<string>& v 输出:
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 312页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
void ShowListInt(list<int>& v)
{
cout << "list<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
#ifdef DECLARATION
练习9.25:对于第312页中删除一个范围内的元素的程序,如果elem1与elem2相等
会发生什么?如果elem2是尾后迭代器,或者elem1和elem2皆为尾后迭代器,又
会发生什么?;
如果elem1和elem2相等,则什么也没发生, 容器保持不变.;
哪怕两个迭代器都是尾后位置(例如end() + 1), 也是如此, 程序也不会出错。;
因此,如果elem1与elem2都是尾后迭代器时,容器保持不变。;
如果elem2为尾后迭代器,elem1指向之前的合法位置,则会删除从elem1开始直至;
容器末尾的所有元素;
#endif
//测试代码
list<int> ilst = { 1,2,3,4,5 };
list<int>::const_iterator elem1 = ilst.begin();
list<int>::const_iterator elem2 = ilst.begin();
ilst.erase(elem1, elem2);//输出内容:list<int>& v 输出:1 2 3 4 5
ShowListInt(ilst);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
#include <vector>
using namespace std;
void ShowListInt(list<int>& v)
{
cout << "list<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
void ShowVecInt(vector<int>& v)
{
cout << "vector<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
#ifdef DECLARATION
练习9.26:使用下面代码定义的ia,将ia拷贝到一个vector和一个list中。使
用单迭代器版本的erase从list中删除奇数元素,从vector中删除偶数元素。
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
#endif
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
int arr_size = sizeof(ia) / sizeof(int);
vector<int> ivec;
ivec.insert(ivec.begin(),ia, ia+arr_size);
for (vector<int>::iterator it = ivec.begin();
it != ivec.end();)
{
if (!(*it & 1))//遍历是偶数时
it = ivec.erase(it);
else
++it;
}
ShowVecInt(ivec);
int *beg = ia, *end = &ia[arr_size]; //指针位置
list<int> ilist;
ilist.assign(beg, end);
for (list<int>::iterator it = ilist.begin();
it != ilist.end();)
{
if (*it & 1)//遍历是奇数时
{
it = ilist.erase(it);
}
else
++it;
}
ShowListInt(ilist);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 313页 单向链表
=====================================================================
//QQ108201645编写
表9.8: 在forward_list中插入或删除元素的操作 | |
lst.before_begin() |
返回指向链表首元素之前不存在的元素的迭代器.此迭代器不能解引用。cbefore_begin()返回一个const_iterator |
lst.cbefore_begin() | |
lst.insert_after(p,t) |
在迭代器p之后的位置插入元素。t是一个对象,n是数量,b和e是表示范围的一对迭代器(b和e不能指向lst内), il是一个花括号列表。返回一个指向最后一个插入元素的迭代器。如果范围为空,则返回p。若p为尾后迭代器,则函数行为未定义。 |
lst.insert_after(p,n,t) | |
lst.insert_after(p,b,e) | |
lst_insert_after(p,il) | |
emplace_after(p,args) |
使用args在p指定的位置之后创建一个元素。返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义 |
lst.erase_after(p) |
删除p指向的位置之后的元素,或删除从b之后直到(但不包含)e之间的元素。返回一个指向被删除之后元素的迭代器。如果p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义 |
lst.erase_after(b,e) |
=====================================================================
第9章 顺序容器 313页 特殊的forward_list操作(删除)
=====================================================================
//QQ108201645编写
#include <iostream>
#include <forward_list>
using namespace std;
void ShowForListInt(forward_list<int>& v)
{
cout << "forward_list<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
forward_list<int> flst = { 0,1,2,3,4,5,6,7,8,9 };
forward_list<int>::const_iterator prev = flst.before_begin();//表示flst的首元素之前
auto curr = flst.begin();//表示flst中的第一个元素
while (curr!=flst.end())
{//forward没有insert、emplace、erase
if (*curr % 2)//若元素为奇数
curr = flst.erase_after(prev);//删除前一个之后的元素并移动curr
else
{
prev = curr;//移动迭代器curr,指向下一个元素,prev指向
++curr;
}
}
ShowForListInt(flst);
//测试代码
forward_list<int> flst1 = { 0,1,2,3,4,5,6,7,8,9 };
for (auto it = flst1.begin(), tmp = flst1.before_begin(); it != flst1.end();)
{
if (!(*it & 1))
{
it = flst1.erase_after(tmp);
}
else
{
tmp = it;
++it;
}
}
ShowForListInt(flst1);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 314页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <forward_list>
using namespace std;
void ShowForListInt(forward_list<int>& v)
{
cout << "forward_list<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
void ShowForListStr(forward_list<string>& v)
{
cout << "forward_list<string>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
int main()
{
/*练习9.27:编写程序,查找并删除forward_list<int>中的奇数元素。*/
forward_list<int> flst = { 0,1,2,3,4,5,6,7,8,9 ,10,11,12,13,14,15,16,17,18,19,20};
for (auto it = flst.begin(), tmp = flst.before_begin(); it != flst.end();)
{
if (*it & 1)//若元素为奇数
{
it = flst.erase_after(tmp);//删除奇数
}
else
{
tmp = it;
++it;
}
}
ShowForListInt(flst);
/*练习9.28:编写函数,接受一个forward_list<string>和两个string共三个参
数。函数应在链表中查找第一个string,并将第二个string插入到紧接着第一个
string之后的位置。若第一个string未在链表中,则将第二个string插入到链表
末尾。*/
void Search_FLst_str(forward_list<string>& sflst, const string& s1, const string& s2);//函数声明
string word = "def", buf = "123";
forward_list<string> sflst = { "abc","def","ghi","jkl","mno","pqi" };
Search_FLst_str(sflst, word, buf);//查找word字符串,如果没有,则把buf插入在末尾。如果有则插入在查找到的后一个元素位置
ShowForListStr(sflst);
Search_FLst_str(sflst, "哈", "找到了吗");
ShowForListStr(sflst);
system("pause");
return 0;
}
void Search_FLst_str(forward_list<string>& sflst, const string& s1, const string& s2)
{
forward_list<string>::iterator prev = sflst.before_begin();//前驱元素
forward_list<string>::iterator curr = sflst.begin();//当前元素
bool inserted = false;
while (curr != sflst.end())
{
if (*curr == s1)
{
curr = sflst.insert_after(curr, s2);//插入在当前之后的
inserted = true;//表示找到
}
else
{
prev = curr;
curr++;
}
}
if (!inserted)//如果未找到,则插入尾后
sflst.insert_after(prev, s2);
}
=====================================================================
//QQ108201645编写
//vector插入方法
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void ShowVecInt(vector<int>& v)
{
cout << "vector<int>& v 输出:";
auto beg = v.begin(), end = v.end();//起始元素开始遍历
while (beg != end)
cout << *beg++ << " ";
cout << endl<<endl;
}
void SearchVce(vector<int>& v, int num, int insertNum)
{
vector<int>::iterator it = v.begin();
bool flag = false;
while (it != v.end())
{
if (*it == num)
{
it = v.insert(it + 1, insertNum);//增加一个位置后插入
++it;
++it;
flag = true;
}
else
{
++it;
}
}
if (!flag)//如果没找到
v.insert(v.end(), insertNum);//插入到尾部
}
//vector删除方法
void DeleteVecInt(vector<int>& v,int num)
{
bool flag = false;
for (vector<int>::iterator it = v.begin();
it != v.end();)
{
if (*it == num)
{
flag = true;
it = v.erase(it);
}
else
{
++it;
}
}
if (!flag)
cout<<"未找到删除元素 = "<<num<<endl;
}
int main()
{
vector<int> ivec{ 1,2,3,4,5,6,7,8,9 };
SearchVce(ivec, 5, 20);//找到5,将20插入5之后
ShowVecInt(ivec);
SearchVce(ivec, 55, 22);//未找55,将20到插入到尾部
ShowVecInt(ivec);
DeleteVecInt(ivec, 6);
ShowVecInt(ivec);
DeleteVecInt(ivec, 60);
ShowVecInt(ivec);
system("pause");
return 0;
}
=====================================================================
#include<forward_list>
#include<string>
#include<iostream>
using namespace std;
//forward_list查找添加内容
void SearchFListStr(forward_list<string> &v, const string& s1, const string& s2)
{
auto curr = v.begin(), prev = v.before_begin();
bool flag = false;
while (curr != v.end())
{
if (*curr == s1)
{
curr = v.insert_after(curr, s2);
++curr;
flag = true;
}
else
{
prev = curr;
++curr;
}
}
if (!flag)
{
cout << "未找到删除元素 = " << s1 << "\t 在末尾插入: " << s2 << endl;
v.insert_after(prev, s2);//prev的下一个就是v.end(),然后插入
}
}
//forward_list查找删除内容
void DeleteFListStr(forward_list<string> &v, const string& s1)
{
forward_list<string>::iterator it = v.begin(), tmp = v.before_begin();
bool flag = false;
while (it != v.end())
{
if (*it == s1)
{
it = v.erase_after(tmp);//删除tmp之后的元素
flag = true;
}
else
{
tmp = it;
++it;
}
}
if (!flag)
cout << "未找到删除元素 = " << s1 << endl;
}
void ShowFlist(forward_list<string>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
forward_list<string> flst{ "head","hello","world" };
SearchFListStr(flst, "hello", "nihao");
ShowFlist(flst);
SearchFListStr(flst, "xx", "有没有");
ShowFlist(flst);
DeleteFListStr(flst, "hello");
ShowFlist(flst);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <string>
#include <list>
#include <forward_list>
#include <iostream>
using namespace std;
void replace_list(list<int>& v1, list<int>& v2, list<int> &v3)
{
auto it1 = v1.begin();
while (it1 != v1.end())
{
size_t flag = 0;
auto itmp = it1, it2 = v2.begin(), it3 = v3.begin();
while (it2 != v2.end())
{
if (*it1 == *it2)
{
flag = 1;
break;
}
else
++it2, ++it3;
}
if (flag == 1)
{
++itmp;
it1 = v1.erase(it1, itmp);
it1 = v1.insert(it1, *it3);
}
else
{
++it1;
}
}
}
void ShowLst(list<int>& v)
{
list<int>::iterator it = v.begin();
for (; it != v.end(); ++it)
cout << *it << " ";
cout << endl;
}
void replace_Flist(forward_list<string> &v1, forward_list<string>& v2, forward_list<string>& v3)
{
forward_list<string>::iterator curr = v1.begin(), prev = v1.before_begin();
while (curr != v1.end())
{
forward_list<string>::iterator iter2 = v2.begin(), iter3 = v3.begin();
size_t flag = 0;
while (iter2 != v2.end())
{
if (*curr == *iter2)
{
flag = 1;
break;
}
else
++iter2, ++iter3;
}
if (flag == 1)
{
auto it = curr;
++it;
curr = v1.erase_after(prev, it); /* 从prev的下一个迭代器(就是curr)到it的一个迭代器删除, 或
curr = v1.erase_after(prev); 就可以了*/
curr = v1.insert_after(prev, *iter3);
/*prev = curr;
++curr;//要么要加这两个,要么不用加,多循环一次*/
}
else
{
prev = curr;
++curr;
}
}
}
void ShowFLst(forward_list<string>& v)
{
forward_list<string>::iterator it = v.begin();
for (; it != v.end(); ++it)
cout << *it << " ";
cout << endl;
}
int main()
{//list与forward_list的搜索替换成list或forward_list元素
list<int> ilst = { 1,2,3,4,5,6,7,8,9 }, oldVal = { 5,7,9 }, newVal = { 20,30,40 };
ShowLst(ilst);
replace_list(ilst, oldVal, newVal);
ShowLst(ilst);
forward_list<string> sflst{ "abc","def","hij","hello","world","hello","people" },
oldVal1{ "def","hello","hij" },
newVal1{ "123","456","789" };
ShowFLst(sflst);
replace_Flist(sflst, oldVal1, newVal1);
ShowFLst(sflst);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <string>
using namespace std;
//list版本循环报数,遇3删除
void ShowList(list<string>& v)
{
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
//可以任先一个,并注释另一个ilst1
list<string> ilst1{ "张三","李四","王五","石六","黄七","曹八","陈九","小明","小张" };
//list<string> ilst1{ "1","2","3","4","5","6","7","8","9" };
ShowList(ilst1);
auto beg = ilst1.begin();
size_t num = 1;
while (ilst1.size()>1)
{
if (beg == ilst1.end())
beg = ilst1.begin();
cout<<"报数:"<<num<<"\t";
if (num == 3)
{
cout <<"大小:"<< ilst1.size() << "\t删除" << *beg << endl;
beg = ilst1.erase(beg);
num = 1;
}
else
{
ShowList(ilst1);
++beg;
++num;
}
}
cout << "剩余一个\t";
ShowList(ilst1);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <forward_list>
#include <string>
using namespace std;
//forward_list版本循环报数,遇3删除
size_t size_(forward_list<string>& v)
{
size_t len = 0;
for (auto i : v)
len++;
return len;
}
void ShowList(forward_list<string>& v)
{
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
//可以任先一个,并注释另一个ilst1
forward_list<string> ilst1{ "张三","李四","王五","石六","黄七","曹八","陈九","小明","小张" };
//list<string> ilst1{ "1","2","3","4","5","6","7","8","9" };
ShowList(ilst1);
auto beg = ilst1.begin(),prev=ilst1.before_begin();
size_t num = 1;
while (size_(ilst1)>1)
{
if (beg == ilst1.end())
beg = ilst1.begin();
cout<<"报数:"<<num<<"\t";
if (num == 3)
{
cout <<"大小:"<< size_(ilst1) << "\t删除" << *beg << endl;
beg = ilst1.erase_after(prev);
num = 1;
}
else
{
prev = beg;
ShowList(ilst1);
++beg;
++num;
}
}
cout << "剩余一个\t";
ShowList(ilst1);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 314页 改变容器大小
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
using namespace std;
void ShowList(list<int>& v)
{
cout<<v.size()<<endl;
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
/*resize可以增大或缩小容器,array不支持,经过.resize后,原来大小,如果小于resize当前大小,
则会将新元素添加到容器后,如果大于当前大小则,容器后部的元素将被删除*/
list<int> ilist(10, 42);//10个int;每个的值都是42
ShowList(ilist);
ilist.resize(15);//将5个为0的元素添加到ilist的末尾
ShowList(ilist);
ilist.resize(25, -1);//将10个值为-1的元素添加到ilist的末尾
ShowList(ilist);
ilist.resize(5);//从ilist的5开始(末尾),删除20个元素
ShowList(ilist);
/*输出内容
10
42 42 42 42 42 42 42 42 42 42
15
42 42 42 42 42 42 42 42 42 42 0 0 0 0 0
25
42 42 42 42 42 42 42 42 42 42 0 0 0 0 0 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1
5
42 42 42 42 42
*/
system("pause");
return 0;
}
=====================================================================
表9.9: 顺序容器大小操作 | |
resize()不适用array | |
c.resize(n) |
调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素的值进行初始化 |
c.resize(n,t) |
调整c的大小为n个元素。任何新添加的元素都初始化为值t |
WARNING: 如果resize()缩小容器,则指向被删除元素的迭代器、引用和指针都会失效;对vector、string或deque进行resize()可能导致迭代器、指针和引用失效。 |
=====================================================================
第9章 顺序容器 315页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;
void ShowVec(vector<int>& v)
{
cout<<v.size()<<endl;
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
/*练习9.29:假定vec包含25个元素,那么vec.resize(100)会做什么?如果接下来
调用vec.resize(10)会做什么?*/
vector<int> ivec(25, 5);
ivec.resize(100);//从25开始增加75个元素凑满100个元素,并初始化为0
ShowVec(ivec);
ivec.resize(10);//位置从10开始,删除90个元素
ShowVec(ivec);
/*练习9.30:接受单个参数的resize版本对元素类型有什么限制(如果有的话)?
*/
#ifdef DECLARATION
答:对于元素是类类型, 则单参数resize版本要求该类型必须提供一个默认构造函数
#endif
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 315页 容器操作可能使迭代器失效
=====================================================================
#ifdef DECLARATION
9.3.6容器操作可能使迭代器失效
向容器中添加元素和从容器中删除元素的操作可能会使指向容器的指针、引用或
迭代器失效。一个失效的指针、引用或迭代器将不再表示任何元素。使用失效的指针、引
用或迭代器是一种严重的程序设计错误,很可能引起与使用未初始化指针一样的问题(参
见2.3.2节,第49页)
向容器添加元素后:
如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、
指针和引用都会失效。如果存储空间未重新分配,指向插入位置之前的元素的迭代
器、指针和引用仍有效,但指向插入位置之后的元素的迭代器、指针和引用将会失效。
对于deque。插入到除首尾位置之外的任何位置都会导致迭代器、指针和引用失
效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不
会失效
对于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代
器)、指针和引用仍然有效。
当我们从一个容器中删除元素后,指向被删除元素的迭代器、指针和引用会失效。
当我们删除一个元素后:
对于list和forward_list,指向容器其他位置的迭代器(包括尾后迭代器和
首前迭代器)、指针和引用仍然有效
对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他
元素的迭代器、引用或指针也会失效。如果是删除deque的尾元素,则尾后迭代
器也会失效,但其他迭代器、引用和指针不受影响;如果是删除首元素,这些也不
会受影响。
对于vector和string,指向被删除元素之前元素的迭代器、引用和指针仍有效。
注意:当我们删除元素时,尾后迭代器总是会失效。
#endif
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;
void ShowVec(vector<int>& v)
{
cout << "v.size() = " << v.size() << endl;
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
//傻瓜循环,删除偶数元素,复制每个奇数元素
vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };
auto iter = vi.begin();//调用begin而不是cbegin,因为我们要改变vi
while (iter!=vi.end())
{
if (*iter % 2)//当除以2的余数不等于0时是奇数
{
iter = vi.insert(iter, *iter);//复制当前元素插入
iter += 2;//向前移动迭代器,跳过当前元素及插入到它之前的元素
}
else//当是偶数时
iter = vi.erase(iter);//删除偶数元素
//在调用erase后,不必递增,因为erase返回的迭代器已经指向序列中下一个元素
}
ShowVec(vi);
#ifdef DECLARATION
vector<int> v = { 0,1,2,3,4,5,6,7,8,9 };
auto begin = v.begin(), end = v.end();//保存尾迭代器是一个坏主意
while (begin != end)
{
//做一些处理
//插入新值
++begin;//向前移动begin,因为我们想在些元素之后插入元素
begin = v.insert(begin, 42);//插入新值(尾迭代器失效)
++begin;//向前移动begin跳过我们刚刚加入的元素
}
ShowVec(vi);
#endif
//修改为
vector<int> v = { 0,1,2,3,4,5,6,7,8,9 };
auto begin = v.begin(), end = v.end();//保存尾迭代器是一个坏主意
while (begin != end)
{
//做一些处理
//插入新值
if (*begin & 1)
{
++begin;//向前移动begin,因为我们想在些元素之后插入元素
begin = v.insert(begin, 42);//插入新值
++begin;//向前移动begin跳过我们刚刚加入的元素
//插入元素后,尾迭代器失效,重新赋予
end = v.end();
}
else
++begin;
}
ShowVec(vi);
/*
或者把
while(begin!=end)
改成
while(begin!=v.end())每次都重新调用
*/
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <forward_list>
#include <iostream>
using namespace std;
//forward删除查找到的当前元素
void Search_flist_Del(forward_list<int>& v,int num)
{
auto curr = v.begin(), prev = v.before_begin();
while (curr!=v.end())
{
if (*curr == num)
{
curr = v.erase_after(prev);
}
else
{
prev = curr;
++curr;
}
}
}
//forward插入在当前之后的元素
void Search_flist_Insert(forward_list<int>& v, int num, int newVal)
{
auto curr = v.begin();
while (curr != v.end())
{
if (*curr == num)
{
curr = v.insert_after(curr,newVal);
}
else
{
++curr;
}
}
}
void ShowFlst(forward_list<int>& v)
{
for (int i : v)
{
cout << i << " ";
}
cout<<endl;
}
int main()
{
forward_list<int> iflst{ 1,2,3,4,5,6 };
Search_flist_Del(iflst,5);
ShowFlst(iflst);
Search_flist_Insert(iflst, 6, 66);
ShowFlst(iflst);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 317页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <vector>
#include <forward_list>
using namespace std;
void ShowList(list<int>& v)
{
cout << "v.size() = " << v.size() << endl;
for (auto i : v)
cout << i << " ";
cout << "\n";
}
void ShowFList(forward_list<int>& v)
{
int size_ = 0;
for (auto i : v)//测量长度
size_++;
cout << "v.size() = " << size_ << endl;
for (auto i : v)
cout << i << " ";
cout << "\n";
}
int main()
{
/*练习9.31:第316页中删除偶数值元素并复制奇数值元素的程序不能用于list或
forward_list。为什么?修改程序,使之也能用于这些类型。*/
//list的插入与删除
list<int> ilst = { 0,1,2,3,4,5,6,7,8,9 };
vector<int> ivec = { 11,33,55,77,99 };
auto it = ivec.begin();
auto iter = ilst.begin();//首节点
while (iter != ilst.end())
{
if (*iter % 2)//当除以2的余数不等于0时是奇数
{
iter = ilst.insert(iter, *it++);//复制当前元素插入
//iter += 2;//list不支持加减运算
++iter;
++iter;
}
else//当是偶数时
iter = ilst.erase(iter);//删除偶数元素
//在调用erase后,不必递增,因为erase返回的迭代器已经指向序列中下一个元素
}
ShowList(ilst);
//forward_list的插入与删除
it = ivec.begin();
forward_list<int> iflst = { 0,1,2,3,4,5,6,7,8,9 };
auto curr = iflst.begin(), prev = iflst.before_begin();
while (curr != iflst.end())
{
if (*curr & 1)//与1相与结果是奇数时
{
curr = iflst.insert_after(curr, *it++);//复制当前元素插入
prev = curr;
++curr;
}
else//当是偶数时
{
curr = iflst.erase_after(prev);//删除偶数元素
}
//在调用erase后,不必递增,因为erase返回的迭代器已经指向序列中下一个元素
}
ShowFList(iflst);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<forward_list>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
void Show_FLst(forward_list<int>& v)
{
for (auto i : v)
cout << i << " ";
cout<<endl;
}
void Search_Insert_Del(forward_list<int> &v,int num)
{
forward_list<int>::const_iterator curr = v.begin(),//首元素
prev = v.before_begin();//前驱元素
while (curr!=v.end())
{
if (*curr & 1)//当是奇数时
{
curr = v.insert_after(curr, num++);//在curr的下一个位置插入num数,并自增.返回插入num数的位置
prev = curr;//等于当前元素
++curr;//移动一个位置
}
else
curr = v.erase_after(prev);//删除上一个元素的下一个位置并返回当前元素
}
}
void Show_Vec(vector<int>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
void Search_Insert_Del(vector<int> &v, int num)
{
vector<int>::const_iterator curr = v.begin();//首元素
while (curr != v.end())
{
if (*curr & 1)//当是奇数时
{
curr = v.insert(curr+1, num++);//在curr的下一个位置插入num数,并自增.返回插入num数的位置
++curr;//移动一个位置
}
else
curr = v.erase(curr);//删除上一个元素的下一个位置并返回当前元素
}
}
int main()
{
//forward_list查找添加、删除内容
forward_list<int> fslist = { 1,2,3,4,5,6,7,8,9 };
Show_FLst(fslist);
Search_Insert_Del(fslist, 11);
Show_FLst(fslist);
//vector查找添加、删除内容
vector<int> ivec = { 1,2,3,4,5,6,7,8,9 };
Show_Vec(ivec);
Search_Insert_Del(ivec, 21);
Show_Vec(ivec);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
void Show_Vec(vector<int>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
/*练习9.32:在第316页的程序中,像下面语句这样调用insert是否合法?如果不合法,
为什么?
iter = vi.insert(iter, *iter++);
首先对*iter求值,传递给insert第二个形参,此时iter已指向向前奇数的下一个元素
因此传递给insert的第一个参数的迭代器指向的错误位置,程序执行会发生混乱,最终崩溃。
因此,若将代码改为iter=vi.insert(iter++,*iter);或是使用由左至右求值、传递参数的编译器,
代码运行是正确的,但无逻辑性
*/
vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };
vector<int> v2 = { 11,33,55,77,99 };
auto it = v2.begin();
auto iter = vi.begin();//调用begin而不是cbegin,因为我们要改变vi
while (iter != vi.end())
{
if (*iter % 2)//当除以2的余数不等于0时是奇数
{
iter = vi.insert(iter+1, *it++);//当改成这样后
//iter += 2;//向前移动迭代器,跳过当前元素及插入到它之前的元素
//上面这句要改,否则在最后+2后位置变动指向错误位置
++iter;
}
else//当是偶数时
iter = vi.erase(iter);//删除偶数元素
//在调用erase后,不必递增,因为erase返回的迭代器已经指向序列中下一个元素
}
Show_Vec(vi);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
void Show_Vec(vector<int>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
/*练习9.33:在本节最后一个例子中,如果不将insert的结果赋予begin,将会发生
什么?编写程序,去掉此赋值语句,验证你的答案。
容器插入、删除操作都会失败
*/
vector<int> v = { 1,2,3,4,5,6,7,8,9 };
auto begin = v.begin(), end = v.end();
while (begin != v.end())
{
if (*begin & 1)
{
/*begin =*/ v.insert(begin + 1, 42);//如果注释掉begin = 将报错
++begin;
}
else
{
++begin;
}
}
Show_Vec(v);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
void Show_Vec(vector<int>& v)
{
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
/*练习9.34:假定vi是一个保存int的容器,其中有偶数值也有奇数值,分析下面循环
的行为,然后编写程序验证你的分析是否正确。
*/
#ifdef DECLARATION
vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };
auto iter = vi.begin();
while (iter != vi.end())
if (*iter % 2)
iter = vi.insert(iter, *iter);
++iter;//无限循环
#endif
vector<int> vi = { 1,2,3,4,5,6,7,8,9 };
auto iter = vi.begin();
while (iter != vi.end())
if (*iter % 2)
{
iter = vi.insert(iter + 1, *iter);
++iter;
}
else//添加一个else
++iter;
Show_Vec(vi);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 318页 管理容量的成员函数
=====================================================================
//QQ108201645编写
表9.10:容器大小管理 | |
shrink_to_fit只适用于vector、string和deque。 | |
capacity和reserve只适用于vector和string。 | |
c.shrink_to_fit() |
请将capacity()减少为与size()相同大小 |
c.capacity() |
不重新分配内存空间的话,c可以保存多少元素 |
c.reserve() |
分配至少能容纳n个元素的内存空间 |
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> ivec;
//size应该为0;capacity的值依赖于具体实现
cout<<" ivec: size: "<<ivec.size()
<<" capacity: "<<ivec.capacity()<<endl;
//向ivec添加24个元素
for (vector<int>::size_type ix = 0; ix != 24; ++ix)
{
ivec.push_back(ix);
}
//size应该为24,capacity应该大于等于24,具体值依赖于标准库实现
cout << " ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;
/*
vs2017 输出内容
ivec: size: 0 capacity: 0
ivec: size: 24 capacity: 28
*/
ivec.reserve(50);//将capacity至少设定为50,可能会更大
//size应该为24;capacity应该大于等于50,具体依赖标准库实现
cout << " ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;
while (ivec.size()!=ivec.capacity())
{
ivec.push_back(0);
}
//capacity应该未改变,size和capacity不相等
cout << " ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;
/*
输出内容
ivec: size: 0 capacity: 0
ivec: size: 24 capacity: 28
ivec: size: 24 capacity: 50
ivec: size: 50 capacity: 50
*/
//再添加一个新元素
ivec.push_back(42);
//size应该为51,capacity应该大于等于51,具体依赖标准库实现
cout << " ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;
/*
输出内容
ivec: size: 0 capacity : 0
ivec : size : 24 capacity : 28
ivec : size : 24 capacity : 50
ivec : size : 50 capacity : 50
ivec : size : 51 capacity : 75
*/
#ifdef DECLARATION
deque、vector或string可以调用shrink_to_fit退回多余的内存
#endif
//调用shrink_to_fit把vector将超出当前大小的多余内存退回给系统
ivec.shrink_to_fit();//归还内存
cout << " ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;
/*
输出内容
ivec: size: 0 capacity : 0
ivec : size : 24 capacity : 28
ivec : size : 24 capacity : 50
ivec : size : 50 capacity : 50
ivec : size : 51 capacity : 75
ivec : size : 51 capacity : 51
*/
system("pause");
return 0;
}
=====================================================================
#ifdef DECLARATION
ivec的状态如下
0 |
1 |
2 |
… |
23 |
保留空间 |
0~23是ivec.size() 保留空间是ivec.capacity()
#endif
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
/*练习9.35:解释一个vector的capacity和size有何区别。
capacity返回已经为vector分配了多大内存空间(单位是元素大小),也就
是在不分配新空间的情况下,容器可以保存多少个元素。而size则返回容器当前已
经保存了多少个元素
练习9.36:一个容器的capacity可能小于它的size吗?
不可能
练习9.37:为什么list或array没有capacity成员函数?
array和list与vector在数据结构上的差异导致内存分配方式不同
list是链表, 当有新元素加入时, 会从内存空间中分配一个新节点保存它; 当
从链表删除元素时, 该节点占用的内存空间会被立刻释放.因此, 一个链表占用
的内存空间总是与它当前保存的元素所需空间相等(换句话说, capacity总是等
于size)
而array是固定大小数组, 内在一次性分配, 大小不变, 不会变化
因此它们均不需要capacity
*/
/*练习9.38:编写程序,探究在你的标准实现中,vector是如何增长的。*/
void AddVec(vector<int>& v);//函数先声明,后调用
vector<int> ivec{ 1,2,3,4,5,6,7,8,9,10 };
cout << "size = " << ivec.size() << " capacity = " << ivec.capacity() << endl;
ivec.reserve(50);//分配容纳到50个元素的内存
cout << "size = " << ivec.size() << " capacity = " << ivec.capacity() << endl;
ivec.resize(20);//调整当前元素+10
cout << "size = " << ivec.size() << " capacity = " << ivec.capacity() << endl;
AddVec(ivec);
AddVec(ivec);
AddVec(ivec);
/*
练习9.39:解释下面程序片段做了什么:
vector<string> svec;
svec.reserve(1024); //为该vevtor分配1024个元素(字符串)空间
string word;
while (cin >> word)
svec.push_back(word);//元素压入容器
svec.resize(svec.size() + svec.size() / 2); //将可以容纳1024个string的vector扩大0.5倍
*/
/*
练习9.40:如果上一题的程序读入了256个词,在resize之后容器的capacity
可能是多少?如果读入了512个、1000个、或1048个词呢?*/
vector<string> svec;
svec.reserve(1024); //为该vevtor分配1024个元素(字符串)空间
string word="abc";
for (size_t i=1;i<=256;i++)
svec.push_back(word);//元素压入容器
cout << "size = " << svec.size() << " capacity = " << svec.capacity() << endl;
svec.resize(svec.size() + svec.size() / 2); //将可以容纳1024个string的vector扩大0.5倍
cout << "size = " << svec.size() << " capacity = " << svec.capacity() << endl;
/*
如果读入了256个或512个词,capacity 仍然是 1024
如果读入了1000或1048个词,capacity 取决于具体实现。
*/
system("pause");
return 0;
}
void AddVec(vector<int>& v)
{
size_t s = 0, c = 0;
for (s = v.size(), c = v.capacity(); s < c; s++)
v.push_back(1);
cout << "空间:" << v.capacity() << " 元素数:" << v.size() << endl;
}
=====================================================================
第9章 顺序容器 320页 额外的string操作
=====================================================================
//QQ108201645编写
表9.11: 构造string的其他方法 | |
n、len2和pos2都是无符号值 | |
string s(cp,n) |
s是cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符 |
string s(s2,pos2) |
s是string s2从下标pos2开始的字符的拷贝。若pos2>s2.size(),构造函数的行为未定义 |
string s(s2,pos2,len2) |
s是string s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()~pos2个字符 |
=====================================================================
第9章 顺序容器 320页 额外的string操作
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
const char* cp = "Hello World!!!";//以空格字符结束的数组
char noNull[] = { 'H','i' };//不是以空格结束的
string s1(cp);//拷贝cp中的字符直到遇到空字符; s1="Hello World";
string s2(noNull,2);//从noNull拷贝两个字符;s2="Hi"
string s3(noNull);//未定义:noNull不是以空字符结束
string s4(cp + 6, 5);//从cp[6]开始拷贝5个字符;s4="World"
string s5(s1, 6, 5);//从cp[6]开始拷贝5个字符;s5="World"
string s6(s1, 6);//从cp[6]开始拷贝,直至s1末尾;s6="World!!!"
string s7(s1, 6, 20);//正确,只拷贝到s1的末尾;s7="World!!!"
//string s8(s1, 16);//越界,抛出一个out_of_range异常
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 321页 substr操作
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string s("hello world");
string s2 = s.substr(0, 5);//从s[0]开始截取5个字符传递给s2。s2="hello"
string s3 = s.substr(6);//从s[6]开始到末尾的字符传递给s3。s3="world"
string s4 = s.substr(6, 11);//从s[6]开始到末尾的字符传递给s4。s4="world"
//string s5 = s.substr(12);//从s[12]开始截取字符传递给s3,越界,抛出一个out_of_range异常
system("pause");
return 0;
}
=====================================================================
表9.12:字符串的操作 | |
s.substr(pos,n) |
返回一个string,包含从pos开始的n个字符的拷贝。pos默认值为0。n的默认值为s.size()-pos,即拷贝从pos开始的所有字符 |
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{//string支持顺序容器的赋值运算符以及assign、insert和erase操作
string s("hello");
s.insert(s.size(), 5, '!');//在s末尾插入5个感叹号
cout<<s<<endl;
s.erase(s.size() - 5, 5);//从s删除最后5个字符
cout << s << endl;
const char* cp = "Stately, plump Buck";
s.assign(cp, 7);//s=="Stately"
cout << s << endl;
string buf = "abcdef";
s.assign(buf, 1, 2);//从buf[1]开始截取2个字符替换给s
cout<<s<<endl;
s.insert(s.size(), cp + 7);//把cp指针的位置+7开始到末尾的字符插入到s,s=="Stately, plump Buck"
cout << s << endl;
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 322页 改变string的其他方法
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string s = " some string ", s2 = " some other string ", s3 = "123";
s.insert(0, s2);//在s中位置0之前插入s2的拷贝
cout<<s<<endl;
//在s[0]之前插入s3中s3[0]开始的s3.size()个字符
s.insert(0, s3, 0, s3.size());
cout<<s<<endl;
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 322页 append和replace函数
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
int main()
{
//append是在末尾追加(也可以理解为末尾插入)
string s("C++ Primer"), s2 = s;//将s初始化为"C++ Primer",并把s的内容拷贝给s2
s.insert(s.size(), " 4th Ed.");//在s的末尾追加" 4th Ed."。结果s="C++ Primer 4th Ed."
s2.append(" 4th Ed.");//等价方法,将" 4th Ed."追加到s2的末尾
cout<<"s = "<<s<<"\n s2 = "<<s2<<endl;
if (s==s2)
cout<<"s与s2一样"<<endl;
//replace操作是调用erase和insert的一种简写形式
//将"4th"替换为"5th"的等价方法
s.erase(11, 3);//s="C++ Primer Ed."
cout<< "s = " << s<<endl;
s.insert(11, "5th");//s="C++ Primer 5th Ed."
cout<< "s = " << s<<endl;
s2.replace(11, 3, "5th");//与上两个等价方法
/*调用replace时,插入的文本长度可以更长或更短的string*/
cout << "s2 = " << s2 << endl;
s.replace(11, 3, "Fifth");//把s[11]开始的位置的3个字符替换成 "Fifth"
/*在这此调用中,删除了3个字符,但在其位置插入了5个字符*/
cout<< "s = " << s<<endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
表9.13:修改string的操作 | |||||
s.insert(pos,args) |
在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用;接受迭代器的版本返回指向第一个插入的迭代器 | ||||
s.erase(pos,len) |
删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用 | ||||
s.assign(args) |
将s中的字符替换为args指定的字符。返回一个指向s的引用 | ||||
s.append(args) |
将args追加到s。返回一个指向s的引用 | ||||
s.replace(range,arge) |
删除s中范围range内的字符,替换为args指定的字符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用 | ||||
args可以是下列形式之一;append和assign可以使用所有形式 | |||||
str不能与s相同,迭代器b和e不能指向s | |||||
str |
字符串str | ||||
str, pos, len |
str中从pos开始最多len个字符 | ||||
cp, len |
从cp指向的字符数组的前(最多)len个字符 | ||||
cp |
cp指向的以空字符结尾的字符数组 | ||||
n, c |
n个字符c | ||||
b, e |
迭代器b和e指定的范围内的字符 | ||||
初始化列表 |
花括号包围的,以逗号分隔的字符列表 | ||||
replace和insert所允许的args形式依赖于range和pos是如何指定的 | |||||
replace |
replace |
insert |
insert |
args可以是 | |
(pos, len, args) |
(b, e, args) |
(pos, args) |
(iter, args) |
| |
是 |
是 |
是 |
否 |
str | |
是 |
否 |
是 |
否 |
str, pos, len | |
是 |
是 |
是 |
否 |
cp, len | |
是 |
是 |
否 |
否 |
cp | |
是 |
是 |
是 |
是 |
n, c | |
否 |
是 |
否 |
是 |
b2, e2 | |
否 |
是 |
否 |
是 |
初始化列表 | |
=====================================================================
第9章 顺序容器 324页 改变string的多重函数
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
/*练习9.43:编写一个函数,接受三个string参数是s、oldVal 和newVal。使用迭代
器及insert和erase函数将s中所有oldVal替换为newVal。测试你的程序,用
它替换通用的简写形式,如,将"tho"替换为"though",将"thru"替换为"through"。*/
string replace_Str(string& s,string& oldVal, string &newVal)
{
basic_string<char>::iterator iter = s.begin();
//basic_string<char>::iterator iter等价于string::iterator iter
while (iter!=s.end())
{
string::iterator iter1 = iter,//临时变动的迭代器iter1
iter2 = oldVal.begin();//初始化oldVal迭代器
while (iter2 !=oldVal.end()&&*iter1==*iter2)
{//当oldValIt未到达结尾,并且每一个值与迭代器tIt相同时
++iter1;
++iter2;
if (iter1==s.end())//如果到达结尾则跳出结束
break;
}
if (iter2 == oldVal.end())//当iter2到达结尾,说明前者比较的长度一样。
{
#ifdef DECLARATION
s.replace(iter, iter +oldVal.size(),newVal);
//将iter开始到iter+oldVal大小的结束尾置替换成newVal一句替换就可以了
#endif
#ifndef DECLARATION
//第二种
iter = s.erase(iter, iter1);//删除从iter迭代器开始到iter2迭代器结束
if (newVal.size())//判定替换的子串是否为空
{
iter2 = newVal.end();//遍历newVal中的字符
do
{
--iter2;
iter = s.insert(iter, *iter2);//每次插入一个字符
} while (iter2 > newVal.begin());
}
#endif
iter += newVal.size();//迭代器移动到新插入内容之后
}
else
++iter;//如果没找到,迭代器移动到下一个元素
}
return s;
}
/*
练习9.44:重写上一题的函数,这次使用一个下标和replace。
*/
string replace_string(string& s,const string& oldVal,const string &newVal)
{
for (size_t i = 0; i < s.size(); ++i)
{
size_t t = i, j = 0;
while (j!=oldVal.size()&&s[t]==oldVal[j])
{
t++;
j++;
}
if (j == oldVal.size())
{
s.replace(i, oldVal.size(), newVal);//从i位置开始,大小的oldVal.size()个字符替换成newVal
i += newVal.size()-1;//下标调整到新插入内容之后
}
}
return s;
}
string replace_string1(string& s, const string& oldVal, const string &newVal)
{
string::size_type i = 0;
while ((i=s.find(oldVal,i))!=string::npos)//在s中查找oldVal,如果是到达string::npos表示没找到
{
s.replace(i, oldVal.size(), newVal);//将找到的子串替换成newVal的内容
i += newVal.size();// 下标调整到新插入内容之后
}
return s;
}
int main()
{
string s = "thowho ", oldVal = "who", newVal = "12345";
cout << replace_Str(s, oldVal, newVal) << endl;
cout << replace_string(s, "12", "xxx")<<endl;
cout << replace_string1(s, "45", "hello") << endl;
/*
输出结果:
tho12345
thoxxx345
thoxxx3hello
*/
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<iostream>
using namespace std;
string delete_string(string& s, const string &oldVal)
{
size_t i = 0, flag = 0;
while ((i = s.find(oldVal)) != string::npos)
{
flag = i;
s.erase(i, oldVal.size());
}
cout << (!flag ? " 没找到:" : " 已找到并删除了:") << oldVal << endl;
return s;
}
string replace_string(string& s, const string &oldVal, const string& newVal)
{
string::iterator iter = s.begin();
while (iter != s.end())
{
basic_string<char>::iterator iter1 = iter;
string::const_iterator iter2 = oldVal.begin();//const_iterator 表示不能通过迭代器改变元素内容
while (iter2 != oldVal.end() && *iter1 == *iter2)
{
++iter1;
++iter2;
}
if (iter2 == oldVal.end())
{
iter = s.erase(iter, iter1);
iter2 = newVal.begin();
while (iter2 != newVal.end())
{
iter=s.insert(iter, *iter2++);
++iter;//每插入一个字符后iter移动到下一个字符
}
}
else
++iter;
}
return s;
}
int main()
{
string s = "1234567123234";
cout << delete_string(s, "1253") << endl;
cout << delete_string(s, "123") << endl;
cout << replace_string(s, "672", "abc") << endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
/*练习9.45:编写一个函数,接受一个表示名字的string参数和两个分别表示前缀(如
"Mr."或"Ms.")和后缀(如"Jr."或"III")的字符串。使用迭代器及insert和append
函数将前缀和后缀添加到给定的名字中,将生成的新string返回。*/
void Name_string(string &s,const string& prefix,const string &suffix)
{
s.insert(s.begin(), 1, ' ');//从迭代器begin()位置插入一个字节
/*
vector<string> svec;
svec.push_back(s);
svec.insert(svec.begin(), "abc");//vector的insert支持迭代器+字符串,string不支持
*/
//如果用s.insert(s.begin(),prefix);则错误,string没有这样的
s.insert(s.begin(),prefix.begin(),prefix.end());
//从s.begin()位置开始插入(prefix.begin()开始到prefix.end()结的内容)等价于s.insert(0, prefix)
s.append(" ");
s.append(suffix.begin(), suffix.end());
}
/*练习9.46:重写上一题的函数,这次使用位置和长度来管理string,并只使用insert。*/
void in_Name(string& s, const string& prefix, const string& suffix)
{
s.insert(0, " ");
s.insert(0, prefix);
s.append(" ");
s.append(suffix);
}
void Process(string& s)
{
Name_string(s, "Mr.", "II");
cout << s << endl;
s = "M";
Name_string(s, "Mrs.", "III");
cout << s << endl;
}
int main()
{
string s = "Tom Bond";
string s1 = s;
Process(s);
Process(s1);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 325页 string搜索操作
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
/*string类提供了6个不同的搜索函数,每个函数都有4个版本,每个搜索操作都返回一个string::size_type
值,表示匹配发生位置的下标,标准库将npos定义为一个const string::size_type类型,并始化为-1。
由于npos是一个unsigned类型,意味着npos等于任何string最大的可能大小(参见2.1.2节,第32页)*/
//string搜索函数返回string::size_type
string name("AnnaBelle");
string::size_type pos = name.find("Anna");//pos==0
//搜索字符串"Anna"在"AnnaBelle"中第一次出现的下标位置(注意大小写)
string lowercase("annabelle");
pos = lowercase.find("Anna");//pos==npos;
//这段代码会将pos置为npos,因为"Anna"和"anna"不匹配
//一个更复杂一些的问题是查找与给定字符中任何一个字符匹配位置
string numbers("0123456789"),my_name("r2d2abcdef2");
//下面代码是定义my_name中的第一个数字
pos = my_name.find_first_of(numbers);//在my_name中查找numbers里内容第一个出现的位置
cout<<pos<<endl;//pos==1
pos = my_name.find_first_of(numbers, 5);//在my_name中第5个位置查找numbers里内容第一个出现的位置
cout<<pos<<endl;//pos==10
string dept("03714p3fj45");
pos = dept.find_first_not_of(numbers);//在dept中查找第一个不是numbers里的内容的位置,返回5,字符p的下标
cout << pos << endl;//pos==5
pos = dept.find_first_not_of(numbers,2);//在dept第2个位置开始查找numbers里不是numbers里的内容的位置
cout<<pos<<endl;//pos==5
pos = dept.find_last_of(numbers);//在dept中查找最后一个是numbers里任意一个字符的位置
cout<<pos<<endl;//pos==10
pos = dept.find_last_not_of(numbers);//在dept中查找最后一个不是numbers里任意一个字符的位置
cout << pos << endl;//pos==8
system("pause");
return 0;
}
=====================================================================
表9.14: string搜索操作 | |
搜索操作返回指定字符出现的下标,如果未找到则返回npos | |
s.find(args) |
查找s中args第一次出现的位置 |
s.rfind(args) |
查找s中args最后一次出现的位置 |
s.find_first_of(args) |
在s中查找args中任何一个字符第一次出现的位置。 |
s.find_last_of(args) |
在s中查找args中任何一个字符最后一次出现的位置。 |
s.find_first_not_of(args) |
在s中查找第一个不在args中的字符 |
s.find_last_not_of(args) |
在s中查找最后一个不在args中的字符 |
args必须是以下形式之一 | |
c, pos |
从s中位置pos开始查找字符c。pos默认为0 |
s2, pos |
从s中位置pos开始查找字符串s2。pos默认为0 |
cp, pos |
从s中位置pos开始查找指针cp指向的以空字符结尾的c风格字符串。pos默认为0 |
cp, pos, n |
从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n无默认值 |
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
string numbers("123456789");
string name("123annab123ell123e");
string::size_type pos = 0;
//每步循环查找name中下一个数
while ((pos=name.find_first_not_of(numbers,pos))
!=string::npos)//查找name中(numbers,pos)从pos开始的位置中不是numbers里的数字的字符,并输出
{
cout<<" found number at index: "<<pos
<<"element is "<<name[pos]<<endl;//输出不是数字的元素
++pos;//移动到下一个字符
}
cout<<name<<endl;//name="123annab123ell123e"
while ((pos=name.find_first_not_of(numbers))!=string::npos)
{//删除name中不是数字的内容
//添加一个删除
name.erase(pos, 1);//删除pos所在位置的1个字符
}
cout<<"删除后输出:"<<name<<endl;//name="删除后输出:123123123"
//第二种写法
string buf = "abc123def456", s1 = "xxx";
string::iterator iter1 = buf.begin();
while (iter1!=buf.end())
{
size_t flag = 0;
string::iterator iter2 = numbers.begin();
while (iter2!=numbers.end())
{
if (*iter1 == *iter2)//逐个字符比较
{
flag = 1;//表示成立
break;
}
++iter2;
}
if (flag == 1)
{
iter1 = buf.erase(iter1, iter1 + 1);
}
else
++iter1;
}
cout<<buf<<endl;//buf="abcdef"
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
string rever("Mississippi");
auto first_pos = rever.find("is");//返回1
auto last_pos = rever.find("is");//返回4
/*
find_last_of搜索与给定string中任何一个字符匹配的最后一个字符
find_last_not_of搜索最后一个不出现在给定string中的字符
*/
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void find_char(string& s, const string& chars)
{
cout << "在" << s << "中查找" << chars << "中字符" << endl;
string::size_type pos = 0;
while ((pos=s.find_first_of(chars,pos))!=string::npos)
{
//找到字符
cout<<"pos: "<<pos<<",char: "<<s[pos]<<endl;
pos++;//移动到下一个字符
}
}
void find_char1(string& s, const string& chars)
{
cout << "在" << s << "中查找不在" << chars << "中字符" << endl;
string::size_type pos = 0;
while ((pos = s.find_first_not_of(chars, pos)) != string::npos)
{
//找到字符
cout << "pos: " << pos << ",char: " << s[pos] << endl;
pos++;//移动到下一个字符
}
}
int main()
{
/*练习9.47:编写程序,首先查找string"ab2c3d7R4E6"中每个数字字符,然后查
找其中每个字母字符。编写两个版本的程序,第一个要使用find_first_of,第二个
要使用find_first_not_of。*/
string s = "ab2c3d7R4E6";
cout<<"查找所有数字"<<endl;
find_char(s,"0123456789");
cout<<endl<<"查找所有字母"<<endl;
find_char(s, "abcdefghijklmnopqrstuvwxyz"\
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
cout << endl;
find_char1(s, "0123456789");
cout << endl << "查找所有字母" << endl;
find_char1(s, "abcdefghijklmnopqrstuvwxyz"\
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
/*练习9.48:假定name和numbers的定义如325页所示,numbers.find(name)返回什么?
如果不与name匹配则返回string::npos*/
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using namespace std;
void find_char(string& s, const string& chars)
{
cout << "在" << s << "中查找\n" << chars << "中字符" << endl;
string::size_type pos = 0;
while ((pos = s.find_first_of(chars, pos)) != basic_string<char>::npos)
{
cout << "pos: " << pos << ",chars: " << s[pos] << endl;
++pos;
}
}
void find_string_local(string& s, const string& chars)
{
cout << "在" << s << "中查找\n" << chars << "中字符的位置" << endl;
for (string::iterator it = s.begin(); it != s.end(); ++it)
{
for (size_t j = 0; j < chars.size(); ++j)
{
if (*it == chars[j])
{
cout << "local: " << j + 1 << ",chars: " << *it << endl;
break;
}
}
}
}
int main()
{
string data = "123456789"\
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"\
"abcdefghijklmnopqrstuvwxyz";
string s = "abc123def456";
find_char(s, data);
find_string_local(s, data);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <cctype>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
void find_longest_word(ifstream& in)
{
string s, longest_word;
size_t max_length = 0;
while (in>>s)//以空格为分隔接收一个内容
{
if (s.find_first_of("bdfghjklpqty") != string::npos)//查找在字符中是否出现
{//如果出现就跳过本次循环
continue;//包含上出头或下出头字母
}
if (max_length < s.size())//成立的话,则记录把记录的与当前的进行比较,长单词则被记录
{
max_length = s.size();//第一次则记录长度和单词,s是接收到字符的长度
longest_word = s;
}
}
cout<<endl<<"最长字符串: "<<longest_word<<endl;
}
int main(int argc,char* argv[])
{
/*
练习9.49:如果一个字母延伸到中线之上,如d 或 f,则称其有上出头部分(ascender)。
如果一个字母延伸到中线之下,如p或g,则称其有下出头部分(descender)。编写程
序,读入一个单词文件,输出最长的既不包含上出头部分,也不包含下出头部分的单词。
*/
//新建一个1.txt输入内容abcdefghij kl mno mnoa pqrstuvwxyz并保存
ifstream in("1.txt");//打开文件
if (!in)
{
cerr<<"无法打开输出文件"<<endl;
return -1;
}
find_longest_word(in);
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 327页 compare函数
=====================================================================
//QQ108201645编写
表9.15:s.compare的几种参数形式 | |
s.compare(s2) |
比较s和s2 |
s.compare(pos1, n1, s2) |
将s中从pos1开始的n1个字符与s2进行比较 |
s.compare(pos1, n1, s2, pos2, n2) |
将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较 |
s.compare(cp) |
比较s与cp指向的以空字符结尾的字符数组 |
s.compare(pos1, n1, cp) |
将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较 |
s.compare(pos1, n1, cp, n2) |
将s中从pos1开始的n1个字符与cp指向的地址开始的n2个字符进行比较 |
=====================================================================
//QQ108201645编写
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
char ch[] = "abc Tom Bond 123", ch1[] = "Tom Bond", *str = ch1;
string s = ch;
string s1 = "Tom Bond", s2 = s1;
cout<<"s.compare(s1) = "
<<s.compare(s1)<<endl;//比较s和s1,是否相同
cout<<"s.compare(4,8,s1) = "
<<s.compare(4,8,s1)<<endl;//从s[4]开始,总共8个字符与s1进行比较
cout << "s.compare(4, 8, s1, 0, 8) = "
<<s.compare(4, 8, s1, 0, 8)<<endl;//从s[4]开始的8个字符与s[0]开始的8个字符比较
cout<<"s1.compare(ch) = "
<<s1.compare(ch)<<endl;//s1与ch数组比较
cout<<"s1.compare(s2) = "
<<s1.compare(s2)<<endl;//s1与s2进行比较
cout << "s.compare(4,8,ch1) = "
<< s.compare(4, 8, ch1) << endl;//从s[4]开始,总共8个字符与ch1进行比较
cout<<"s.compare(4,8,str,8) = "
<< s.compare(4, 8, str, 8)<<endl;//从s[4]开始的8个字符与指针str开始的8个字符比较
/*
输出结果
s.compare(s1) = 1
s.compare(4,8,s1) = 0
s.compare(4, 8, s1, 0, 8) = 0
s1.compare(ch) = -1
s1.compare(s2) = 0
s.compare(4,8,ch1) = 0
s.compare(4,8,str,8) = 0
*/
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 328页 string和数值之间的转换
=====================================================================
//QQ108201645编写
表9.16:string和数值之间的转换 | |
to_string(val) |
一组重载函数,返回数值val的string表示。val可以是任何算术类型(参见2.1.1节,第30页)。对每个浮点类型和int或更大的整型,都有相应版本的to_string。与往常一样,小整数会被提升(参见4.11.1节,第142页) |
stoi(s, p, b) |
返回s的起始子串(表示整数内容)的数值,返回值类型分别是int、long、unsigned long、long long、unsigned long long。b表示转换所用的基数,默认值为10。p是size_t指针,用来保存s中第一个非数值字符的下标,p默认为0,即,函数不保存下标 |
stol(s, p, b) | |
stoul(s, p, b) | |
stoll(s, p, b) | |
stoull(s, p, b) | |
stof(s, p) |
返回s的起始子串(表示浮点内容)的数值,返回值类型分别是float、double或long double。参数p的作用与整数转换函数中一样 |
stod(s, p) | |
stold(s, p) |
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
#include <cctype>
#include <iomanip>
using namespace std;
int main()
{
int i = 42;
string s = to_string(i);//将整数i转换成字符表示形式
double d = stod(s);//将字符串s转换成浮点浮
cout << " d = " << d << endl;
string s1 = "abcABC123.45678";
size_t size;
for (size_t ix = 0; ix < s1.size(); ++ix)
{
if (!isalpha(s1[ix]))//判断是不是字母,是就跳过
{//不是字母则把s1的ix开始的位置截取出来传递到stod,并得到转换数字后的偏移大小,把double值传给d
d = stod(s1.substr(ix), &size);
ix += size;//ix加大小偏移到末尾
}
}
cout<<"d = "<<setprecision(8)<<d<<" size = "<<size<<endl;//控制小数精度为8位,输出d = 123.45678 size = 9
string s2 = "pi = 3.14";
//轮换s2中以数字开始的第一个子串,结果d=3.14
size_t* p = nullptr;
d = stod(s2.substr(s2.find_first_of("+-.123456789")));
cout << "p = " << p << " d = " << d << endl;
s = "270f";//十六进制9999
i = stoi(s, 0, 16);//十六进制输出
cout << "p = " << p << " i = " << i << endl;//输出9999
s = "9999";
i = stol(s, 0, 10);//十进制输出
cout << "p = " << p << " i = " << i << endl;//输出9999
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int AddVecInt(vector<string>& v)
{
int sum = 0;
for (auto i : v)
{
sum += stoi(i);
}
return sum;
}
int AddVecDouble(vector<string>& v)
{
int sum = 0;
for (auto i : v)
{
sum += stod(i);
}
return sum;
}
void ShowVec(vector<int>& v)
{
for (int i : v)
{
cout<<i<<" "<<endl;
}
cout<<endl;
}
int main()
{
/*练习9.50:编写程序处理一个vector<string>,其元素都表示整型值。计算vector
中所有元素之和。修改程序,使之计算表示浮点值的string之和。*/
vector<string> ivec{ "10","20","30","40","50","60","70","80","90" };
cout << AddVecInt(ivec) << endl;
ivec.assign({ "1.1","2.2","3.3","4.4","5.5","6.6","7.7","8.8","9.9" });//替换元素
cout<<AddVecDouble(ivec)<<endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<string> Month_Name = { "January","February","March","April",
"May","June","July","August","September","October","November","December" };
void Display(vector<string>& v)
{
for (vector<string>::iterator it = Month_Name.begin();
it != Month_Name.end(); ++it)
{
for (string::iterator strIt = it->begin();
strIt != it->end(); ++strIt)
cout << *strIt << " ";
cout<<endl;
}
}
int main()
{
Display(Month_Name);
system("pause");
return 0;
}
/*
输出内容
J a n u a r y
F e b r u a r y
M a r c h
A p r i l
M a y
J u n e
J u l y
A u g u s t
S e p t e m b e r
O c t o b e r
N o v e m b e r
D e c e m b e r
*/
=====================================================================
//QQ108201645编写
#include <stack>
#include <deque>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
/*练习9.51:设计一个类,它有三个unsigned成员,分别表示年、月和日。为其编写
构造函数,接受一个表示日期的string参数。你的构造函数应该能处理不同的数据格式,
如January 1, 1900、1 / 1 / 1990、Jan 1 1900 等。*/
class Date
{
unsigned year_, month_, day_;
public:
friend ostream& operator<<(ostream& os, const Date& other);
Date() = default;
Date(string &s);
unsigned int Year()const { return year_; }
unsigned int Month()const { return month_; }
unsigned int Day()const { return day_; }
};
//月份全称
const string Month_name[] = { "January","February","March",
"April","May","June","July","August","September"
"October","November","December" };
//月份简写
const string Month_abbr[] = { "Jan","Feb","Mar","Apr","May",
"Jun","Jul","Aug","Sept","Oct","Nov","Dec" };
//每月天数
const int Days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
inline int get_Month(string &s, int &end_of_Month)
{
size_t i, j;
for (i = 0; i < 12; i++)
{
for (j = 0; j < Month_abbr[i]. size(); j++)
{
if (s[j] != Month_abbr[i][j])//读取与简写是否相同(检查是不是月份简写,三个字符)
break;//不是此月简写
}
if (j == Month_abbr[i].size())//当长度与简写匹配,表示读取完成
break;
}
if (i == 12)//如果i月份大等于12表示已找到末尾结束
{
throw invalid_argument("不是合法月份名");
}
if (s[j] == ' ')//判断是不是空格
{
end_of_Month = j + 1;
return i + 1;
}
for (; j < Month_name[i].size(); ++j)
if (s[j] != Month_name[i][j])
break;
if (j == Month_name[i].size() && s[j] == ' ')
{
end_of_Month = j + 1;
return i + 1;
}
throw invalid_argument("不是合法月份名");
}
inline int get_Day(string &s, int month, int &p)
{
size_t q;
int day = stoi(s.substr(p), &q);//从p开始的部分转换为日期值
if (day<1 || day>Days[month - 1])//月份全称是一个数组,所以必须减1位防止越界
{
throw invalid_argument("不是合法日期值");
}
p += q;
return day;
}
inline int get_Year(string &s, int &p)
{
size_t q;
int year = stoi(s.substr(p), &q);//从p开始的部分转换为年
if (p + q < s.size())
{
throw invalid_argument("非法结尾内容");
}
return year;
}
Date::Date(string &s)
{
int p;
size_t q;
if ((p = s.find_first_of("0123456789")) == string::npos)//查找s里面是否有数字,如果== string::npos表示没有
{
throw invalid_argument("没有数字,非法日期");
}
if (p > 0)
{
month_ = get_Month(s, p);
day_ = get_Day(s, month_, p);
if (s[p] != ' '&&s[p] != ',')
{
throw invalid_argument("非法间隔符");
}
p++;
year_ = get_Year(s, p);
}
else
{
month_ = stoi(s, &q);
p = q;
if (month_ < 1 || month_>12)
throw invalid_argument("不是合法月份值");
if (s[p++] != '/')
throw invalid_argument("非法间隔符");
day_ = get_Day(s, month_, p);
if (s[p++] != '/')
throw invalid_argument("非法间隔符");
year_ = get_Year(s, p);
}
}
ostream& operator<<(ostream& os, const Date& other)
{
os<<other.Year()<<"年"<<other.Month()<<"月"<<other.Day()<<"日"<<endl;
return os;
}
int main()
{
string dates[] = { "Jan 1,2014","February 1 2014","3/1/2014",
// "Jcn 1,2014",
// "Janvary 1 2014",
// "Jan 32,2014",
// "Jan 1/2014",
"3 1 2014" };
try
{
for (auto s : dates)
{
Date ds(s);
cout << ds;
}
}
catch (invalid_argument e)
{
cout<<e.what()<<endl; //what是runtime_error类的一个成员函数
}
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
//简化一下代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//月份全称
vector<string> Month_Name = { "January","February","March","April",
"May","June","July","August","September","October","November","December" };
//每月天数
const int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
class Date
{
public:
Date() = default;
Date(string &s);
friend ostream& operator << (ostream& os, const Date& other)
{
os << other.year << " " << other.month << " " << other.day << endl;
return os;
}
private:
unsigned year, month, day;
};
inline int Get_Month(string &s, int &end_of_month)
{
size_t i, j = 0;
for (i = 0; i < Month_Name.size(); i++)
{
if (s.substr(0, 3) == Month_Name[i].substr(0, 3))//截取前3个字符判定是不是简写
{
j += 3;//是简写就偏移前三个字符
break;
}
}
if (i == 12)
{
throw invalid_argument("不是合法月份名");
}
if (s[j] == ' ')
{
end_of_month = j + 1;//位置+1
return i + 1;//月份+1
}
end_of_month = (int)Month_Name[i].size();//获取月份长度
if (s.substr(0, end_of_month) == Month_Name[i] && s[end_of_month] == ' ')
{//当载取s的月份大小长度的 s的内容与月份字符串相等,并且偏移后的s等于空格时,表示正确
return i + 1;//返回月份加1
}
throw invalid_argument("不是合法月份名");
//当输入即大于三个字符,又与月份不符时或者月份的间隔符不同时,将抛出这个异常
}
inline int Get_Day(string &s, int month, int &p)
{
size_t q;
int day = stoi(s.substr(p), &q);//从p开始的位置截取字符串到末尾,q是传出一个截去的大小
if (day<1 || day>days[month - 1])//月份全称是一个数组,所以必须减1位防止越界
{
throw invalid_argument("不是合法日期");
}
p += q;
return day;
}
inline int Get_Year(string &s, int &p)
{
size_t q;
int year = stoi(s.substr(p), &q);
if (p + q < s.size())
throw invalid_argument("非法结尾内容");
return year;
}
Date::Date(string &s)
{
int p;
size_t q;
if ((p = s.find_first_of("1234567890")) == string::npos)
throw invalid_argument("没有数字,非法日期");
if (p > 0)
{
month = Get_Month(s, p);//如果p>0表示是字符串,就跳转到字符串处理
}
else
{
month = stoi(s, &q);//如果p从0开始表示是数字
if (month < 1 || month>12)
throw invalid_argument("不是合法月份");
if (s[p++] != '/')
{
throw invalid_argument("非法间隔符");
}
}
day = Get_Day(s, month, p);
if (s[p] != ' '&&s[p] != ',')
{
throw invalid_argument("非法间隔符");
}
p++;
year = Get_Year(s, p);
}
int main()
{
string dates[] = { "Jan 1,2014","February,1 2014","3/1,2015",
"3 1 2014" };
try
{
for (auto s : dates)
{
Date date(s);
cout << date;
}
}
catch (invalid_argument valid)
{
cout << valid.what() << endl;//what是runtime_error类的一个成员函数
}
system("pause");
return 0;
}
=====================================================================
第9章 顺序容器 329页 容器适配器
=====================================================================
//QQ108201645编写
表9.17:所有容器适配器都支持的操作与类型 | |
size_type |
一种类型,足以保存当前类型的最大对象的大小 |
value_type |
元素类型 |
container_type |
实现适配器的底层容器类型 |
A a; |
创建一个名为a的空适配器 |
A a (c); |
创建一个名为a的适配器,带有容器c的一个拷贝 |
关系运算符 |
每个适配器都支持所有关系运算符:==、!=、<、<=、>和>=这些运算符返回底层容器的比较结果 |
a.empty() |
若a包含任何元素,返回false,否则返回true |
a.size() |
返回a中的元素数目 |
swap(a, b) |
交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同 |
a.swap(b) |
=====================================================================
第9章 顺序容器 330页 栈适配器
=====================================================================
//QQ108201645编写
#include <stack>
#include <deque>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<string> svec{ "abc","123","hello" };
deque<int> deq{ 1,2,3,4,5,6 };
stack<int> stk(deq);//从deq拷贝元素到std
//在vector上实现的空栈
stack<string, vector<string>>str_stk;
//str_stk2在vector上实现,初始化保存svec的拷贝
stack<string, vector<string>> str_stk2(svec);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <stack>
#include <iostream>
using namespace std;
int main()
{
stack<int> intStack; // 空栈
// 填满栈
for (size_t i = 0; i != 10; ++i)
intStack.push(i); // intStack 保存0~9,十个元素
// intStack中有值继续循环
while (!intStack.empty())
{
int value = intStack.top();
// 使用栈顶值的代码
cout << value << " ";
intStack.pop(); //弹出栈顶元素,继续循环
}
cout<<endl;
system("pause");
return 0;
}
=====================================================================
基于struct结构的栈代码
=====================================================================
//QQ108201645编写
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct Link//链表
{
int data; //链表的数据域是整形
struct Link* next; //链表的指针域是next
};
struct Stack//栈
{
struct Link* head;//指向链表头节点的指针
int size;//栈的大小
};
void StakInit(struct Stack* stack)//栈初始化
{
stack->head = NULL;//头指针指向空
stack->size = 0;//栈大小是0
}
void StackPush(struct Stack* stack, const int data) //入栈操作(往栈当中压入一个数据项data)
{
struct Link* node;//产生一个新的节点
node = (struct Link*)malloc(sizeof(struct Link));// 分配大小为struct Link内存并强制转换成struct Link*类型
assert(node != NULL);//断言不等于空
node->data = data;//新产生节点的数据域是data
node->next = stack->head; //新创建的节点的指针域指向它的头节点
stack->head = node;//头指针指向新创建的节点
++stack->size;// 压入元素之后,栈大小自增1
}
int StackEmpty(struct Stack* stack)
{
return (stack->size == 0);//判定栈是否是空的,栈的大小是否等于0
}
int StackPop(struct Stack* stack, int *data) //出栈操作,弹出来的数据项保存在data当中
{
if (StackEmpty(stack)) //不能够出栈返回0,能够出栈返回1
{
return 0;//不能够出栈返回0
}
struct Link* tmp = stack->head;//保存原来的头节点
*data = stack->head->data;
stack->head = stack->head->next;//头指针指向下一个节点
free(tmp); //原来的头节点释放掉
--stack->size; //栈的大小自减1
return 1;//能够出栈返回1
}
void StackCleanup(struct Stack* stack)
{
struct Link* tmp;
while (stack->head) //遍历链表,指针不等于空就要释放掉
{
tmp = stack->head; //保存原来的头节点
stack->head = stack->head->next; //stack->head指向下一个节点
free(tmp); //tmp释放掉
}
stack->size = 0;
}
int main()
{
int i;
struct Stack stack;
StakInit(&stack);
for (i = 0; i < 5; i++)
{
StackPush(&stack, i);
}
while (!StackEmpty(&stack))
{
StackPop(&stack, &i);
printf("%d ", i);
}
printf("\n");
}
=====================================================================
基于class结构的栈代码
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
/*
struct Link
{
int data;
struct Link* next;
};
struct Stack
{
struct Link* head;
int size;
};
void StackInit(struct Stack* stack)
{
stack->head = NULL;
stack->size = 0;
}
void StackPush(struct Stack* stack, const int data)
{
struct Link* node;
node = (struct Link*)malloc(sizeof(struct Link));
assert(node != NULL);
node->data = data;
node->next = stack->head;
stack->head = node;
++stack->size;
}
int StackEmpty(struct Stack* stack)
{
return (stack->size == 0);
}
int StackPop(struct Stack* stack, int* data)
{
if (StackEmpty(stack))
{
return 0;
}
struct Link* tmp = stack->head;
*data = stack->head->data;
stack->head = stack->head->next;
free(tmp);
--stack->size;
return 1;
}
void StackCleanup(struct Stack* stack)
{
struct Link* tmp;
while (stack->head)
{
tmp = stack->head;
stack->head = stack->head->next;
free(tmp);
}
stack->size = 0;
}
int main(void)
{
struct Stack stack;
StackInit(&stack);
int i;
for (i=0; i<5; i++)
{
StackPush(&stack, i);
}
while (!StackEmpty(&stack))
{
StackPop(&stack, &i);
printf("%d ", i);
}
printf("\n");
return 0;
}
*/
class Stack
{
struct Link
{
int data_;
Link* next_;
Link(int data, Link* next) : data_(data), next_(next) //结构体实际上也是类,也可以定义构造函数的
{
}
};
public:
Stack() : head_(0), size_(0) //head_指针初始化为空==0,栈的大小初始化也是0
{
}
~Stack()
{
Link* tmp;
while (head_)
{
tmp = head_;
head_ = head_->next_;
delete tmp;
}
}
void Push(const int data) //入栈操作(往栈当中压入一个数据项data)
{
Link* node = new Link(data, head_); //产生一个新节点并分配内存,并把data与head_传递进去
head_ = node;
++size_;
}
bool Empty()
{
return (size_ == 0);
}
bool Pop(int& data) //出栈操作,弹出来的数据项保存在data当中
{
if (Empty())//如果不能够出栈返回false,能够出栈返回true
{
return false; //如果是空返回false
}
Link* tmp = head_; //保存原来的头节点
data = head_->data_; //data等于第一个节点的data
head_ = head_->next_; //头指针指向下一个节点
delete tmp; //原来的头节点释放掉
--size_; //栈的大小自减1
return true;
}
private:
Link* head_;
int size_;
};
// 避免名称冲突
// 类型的扩充
// 数据封装、能够保护内部的数据结构不遭受外界破坏
int main(void)
{
Stack stack; // 抽象数据类型 类类型
int i;
for (i = 0; i < 5; i++)
{
//StackPush(&stack, i);
stack.Push(i); // this = &stack
}
//while (!StackEmpty(&stack))
while (!stack.Empty())
{
//StackPop(&stack, &i);
//printf("%d ", i);
stack.Pop(i);
cout << i << " ";
}
//printf("\n");
cout << endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
/*stack类型定义在stack头文件中*/
表9.18:表9.17未列出的栈操作 | |
栈默认基本deque实现,也可以在list或vector之上实现 | |
s.pop() |
删除栈顶元素,但不返回该元素值 |
s.push(item) |
创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,或者由args构造 |
s.emplace(args) | |
s.top() |
返回栈顶元素,但不将元素弹出栈 |
=====================================================================
第9章 顺序容器 330页 队列适配器
=====================================================================
//QQ108201645编写
/*queue和priority_queue类型定义在queue头文件中*/
表9.19:表9.17未列出的queue和priority_queue | |
queue默认基于deque实现,priority_queue默认基于vector实现; | |
queue也可以用list或vector实现,priority_queue也可以用deque实现 | |
q.pop() |
返回queue的首元素或priority_queue的最高优先级的元素,但不删除此元素 |
q.front() |
返回首元素或尾元素,但不删除此元素 |
q.back() |
只适用于deque |
q.top() |
返回最高优先级,但不删除该元素 |
只适用于priority_queue | |
q.push(item) |
在queue末尾或priority_queue中恰当的位置创建一个元素,其值为item,或由args构造 |
q.emplace(args) |
=====================================================================
基于struct结构的queue代码
=====================================================================
//QQ108201645编写
#include <iostream>
#include <cassert>
using namespace std;
struct Link
{
int data; //链表的数据域是整形
struct Link* next; //链表的指针域是next
};
struct Queue
{
struct Link* tail;
struct Link* head;
int size;
};
void QueueInit(struct Queue* queue)
{
queue->tail = NULL;
queue->head = NULL;
queue->size = 0;
}
void QueuePush(struct Queue* queue, const int data)
{
struct Link* node;//产生一个新节点
node = (struct Link*)malloc(sizeof(Link));//分配内存
assert(node != NULL);//断言不等于空
node->data = data;//新产生节点的数据域是data
if (queue->tail)//当不是空时,则把尾节点的next指向新节点
queue->tail->next = node;//尾节点的next指向新创建的节点
queue->tail = node;//尾节点指向新创建的节点(在下一次循环时就可以把上一次的尾节点的next指向当前新节点)
//顺序0 1 2 3 4,head头节点指向0方便Pop时从0开始遍历
++queue->size;
if (!queue->head)//如果是空的话,把queue->head指向尾节点
queue->head = queue->tail;
}
int QueueSize(struct Queue* queue)
{
return queue->size;
}
bool QueueEmpty(struct Queue* queue)
{
return (queue->size == 0);
}
int QueuePop(struct Queue* queue,int &data)
{
if (QueueEmpty(queue))//当不是空时
return 0;
struct Link* tmp = queue->head;//保存准备释放的头节点
data = queue->head->data;//把数据赋值给data(引用传值)
queue->head = queue->head->next;//把头节点指向next节点就是原来指向的新节点
free(tmp);//释放头节点
--queue->size;//大小自减
return 1;
}
void QueueCleanup(struct Queue* queue)
{
struct Link* tmp;
while (!QueueEmpty(queue))
{
tmp = queue->head;
queue->head = queue->head->next;
free(tmp);
}
queue->size = 0;
}
int main()
{
struct Queue queue;
QueueInit(&queue);
int i;
for (i = 0; i < 5; i++)
QueuePush(&queue, i);
cout<<"size = "<<QueueSize(&queue)<<endl;
while (!QueueEmpty(&queue))
{
QueuePop(&queue, i);
cout<<i<<" ";
}
system("pause");
return 0;
}
=====================================================================
基于class结构的queue代码
=====================================================================
//QQ108201645编写
#include<iostream>
#include<cassert>
#include<string>
using namespace std;
class Queue
{
string buf_;
Queue* head_, *tail_, *next_;
size_t size_;
public:
Queue(const string& buf = "")
:buf_(buf), head_(0), tail_(0), next_(0) {}
~Queue()
{
Queue* tmp;
while (head_)
{
tmp = head_;
head_ = head_->next_;
delete tmp;
}
size_ = 0;
}
void Push(const string& buf)
{
Queue* node = new Queue;
node->buf_ = buf;
if (tail_)
tail_->next_ = node;
tail_ = node;
++size_;
if (!head_)
head_ = tail_;
}
bool Empty()
{
return (size_ == 0);
}
int Pop(string& buf)
{
if (Empty())
return false;
Queue* tmp = head_;
buf = head_->buf_;
head_ = head_->next_;
delete tmp;
--size_;
return true;
}
};
int main()
{
Queue queue;
string s[] = { "hello","world","nihao" };
int i;
for (i = 0; i < 3; i++)
queue.Push(s[i]);
while (!queue.Empty())
{
queue.Pop(s[--i]);
}
for (auto i : s)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
=====================================================================
stack和queue代码
=====================================================================
//QQ108201645编写
#include<iostream>
#include<stdlib.h>
#include<cctype>
using namespace std;
class list
{
public:
list *head;
list *tail;
list *next;
int num;
list()
{
head = tail = next = NULL;//初始等于空
}
virtual void store(int i) = 0;//纯虚函数
virtual int retrieve() = 0;//纯虚函数
};
class queue :public list
{
public:
void store(int i);
int retrieve();
};
void queue::store(int i)
{
list *item;//创建一个队列指针
item = new queue;//分配一个queue派生类的内存给item
if (!item)
{
cout << "Allocation error\n";
exit(1);
}
item->num = i;//把i存入num
if (tail)//当是空的时候跳过
tail->next = item;//上一个tail(相当head节点)节点的next 等于新节点
tail = item;//当前尾节点等于新节点
item->next = NULL;
if (!head)//当头节点为空时
head = tail;//头节点等于尾节点。相当等于新节点
}
int queue::retrieve()
{
int i;//创建一个返回值
list *p;//创建一个临时节点
if (!head)
{
cout << "list empty\n";
return 0;
}
i = head->num;//给i赋值
p = head;//保存头节点的位置
head = head->next;//把头节点指向头节点中的next节点
delete p;//删除保存的节点
return i;//返回i的值
}
class stack :public list
{
public:
void store(int i);
int retrieve();
};
void stack::store(int i)
{
list *item;//创建一个栈指针
item = new stack;//分配一个stack派生类的内存给item
if (!item)
{
cout << "Allocation error\n";
exit(1);
}
item->num = i;//把i存入num
if (head)//当是空的时候跳过
item->next = head;//新建的节点等于上一个head节点
head = item;//把头节点指向新节点
if (!tail)//尾节点是空时
tail = head;//尾节点指向头节点
}
int stack::retrieve()
{
int i;
list *p;
if (!head)
{
cout << "list empty\n";
return 0;
}
i = head->num;
p = head;
head = head->next;
delete p;
return i;
}
int main()
{
list *p;
queue qb;
p = &qb;
p->store(1);
p->store(2);
p->store(3);
cout << "Queue:";
cout << p->retrieve();
cout << p->retrieve();
cout << p->retrieve();
cout << '\n';
stack sb;
p = &sb;
p->store(1);
p->store(2);
p->store(3);
cout << "Stack:";
cout << p->retrieve();
cout << p->retrieve();
cout << p->retrieve();
cout << '\n';
system("pause");
return 0;
}
=====================================================================
//摘自习题集第5版练习题答案(另外参考逆波兰计算器:中缀表达式转换为后缀表达式.doc)
#include<iostream>
#include<string>
#include<deque>
#include<stack>
using namespace std;
/*练习9.52:使用stack处理括号化的表达式。当你看到一个左括号,将其记录下来。
当你在一个左括号之后看到一个右括号,从stack中pop对象,直至遇到左括号,将
左括号也一起弹出栈。然后将一个值(括号内的运算结果)push到栈中,表示一个括
号化的(子)表达式已经处理完毕,被其运算结果所替代。*/
enum obj_type
{
LP, RP, ADD, SUB, VAL
};
struct obj
{
obj_type t;
double v;
obj(obj_type type, double val = 0)
{
t = type;
v = val;
}
};
inline void skipws(string &s, size_t &p)
{
p = s.find_first_not_of(" ", p);//查找s里面第一个不是空格的位置
}
inline void newVal(stack<obj> &so, double v)
{
if (so.empty() || so.top().t == LP)//若栈空,或栈顶是左括号,则v是第一个运算数,直接入栈
{
so.push(obj(VAL, v));
cout << "push " << v << endl;
}
else if (so.top().t == ADD || so.top().t == SUB)//否则v前必须是一个运算符号,再之前是一个运算数
{
//之前的运算
obj_type type = so.top().t;
so.pop();//从栈顶弹出运算符
if (type == ADD)
cout << "pop +" << endl;
else
cout << "pop -" << endl;
cout << "pop " << so.top().v << endl;
//执行加减法
if (type == ADD)
v += so.top().v;
else
v = so.top().v - v;
so.pop();//从栈顶弹出运算数
so.push(obj(VAL, v));//运算结果压栈
cout << "push " << v << endl;
}
else
throw invalid_argument("缺少运算符");
}
int main()
{
stack<obj> so;
string s;
size_t p = 0, q;
double v;
cout << "请输入表达式";
getline(cin, s);
while (p < s.size())
{
skipws(s, p);//跳过空格
if (s[p] == '(')
{
so.push(obj(LP));
p++;
cout << "push LP" << endl;
}
else if (s[p] == '+' || s[p] == '-')
{
//新运算符
if (so.empty() || so.top().t != VAL)//空栈或之前不是运算数
throw invalid_argument("缺少运算数");
if (s[p] == '+')
so.push(obj(ADD));
else
{
so.push(obj(SUB));
}
p++;
cout << "push " << s[p - 1] << endl;
}
else if (s[p] == ')')//读入一个右括号
{
p++;
if (so.empty())//若栈空则表示之前没有与之配对
throw invalid_argument("未匹配右括号");
if (so.top().t == LP)//若是左括号,表示括号中间无表达式
throw invalid_argument("空括号");
if (so.top().t == VAL)
{
v = so.top().v;//弹出元素
so.pop();
cout << "pop " << v << endl;
if (so.empty() || so.top().t != LP)//若栈空或不是左括号
throw invalid_argument("未匹配右括号");
so.pop();
cout << "pop LP" << endl;
newVal(so, v);//与新运算符一样
}
else
throw invalid_argument("缺少运算符");//若栈顶不是运算数就抛出一个异常
}
else
{
v = stod(s.substr(p), &q);
p += q;
newVal(so, v);
}
}
if (so.size() != 1 || so.top().t != VAL)
throw invalid_argument("非法表达式");
cout << so.top().v << endl;
system("pause");
return 0;
}
=====================================================================
//简化各种提示(理清逻辑)
#include <iostream>
#include <string>
#include <stack>
using namespace std;
enum obj_type
{
LP='(',RP=')',ADD='+',SUB='-',MUL='*',DIV='/',VAL='n'
};
struct obj
{
char t;
double v;
obj(char type, double val = 0)
:t(type),v(val){}
};
inline void skipws(string& s, size_t& p)
{
p = s.find_first_not_of(" ", p);
}
inline void newVal(stack<obj>& so, double v)
{
if (so.empty() || so.top().t == LP)
{
so.push(obj(VAL, v));
}
else if (so.top().t == ADD || so.top().t == SUB)
{
obj_type type = (obj_type)so.top().t;
so.pop();
if (type == ADD)
v += so.top().v;
else
v = so.top().v - v;
so.pop();
so.push(obj(VAL, v));
}
}
int main()
{
stack<obj> so;
string s;
size_t p=0, t;
double v;
cout << "请输入表达式" << endl;
getline(cin, s);
while (p<s.size())
{
skipws(s, p);//跳过空格
if (s[p] == '+' || s[p] == '-'||s[p]=='(')
{
so.push(obj(s[p]));
p++;
}
else if (s[p] == ')')//读入一个右括号
{
p++;
if (so.top().t == VAL)
{
v = so.top().v;
so.pop();
so.pop();//左括号出栈
newVal(so,v);
}
}
else
{
v=stod(s.substr(p), &t);
p += t;
newVal(so, v);
}
}
if (so.size() != 1 || so.top().t != VAL)
throw invalid_argument("非法表达式");
cout << so.top().v << endl;
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include<iostream>
#include<string>
#include<cassert>
using namespace std;
//逆波兰计算器
class Stack
{
struct Link
{
double number_;
char buf_;
struct Link* next_;
Link(char buf, double number,struct Link* next)
:buf_(buf),number_(number),next_(next){}
};
public:
Stack():size(0),head(0){}//head_指针初始化为空==0,栈的大小初始化也是0
~Stack()
{
Link* tmp;
while (head)
{
tmp = head;
head = head->next_;
delete(tmp);
}
size = 0;
}
void Push(double number)
{
Link* node = new Link('0',number, head);//产生一个新节点并分配内存,并把char、number与head_传递进去
head = node;
++size;
}
void Push(char& buf)//重载类体内的成员函数
{
Link* node = new Link(buf,0,head);//产生一个新节点并分配内存,并把char、number与head_传递进去
head = node;
++size;
}
bool Empty()
{
return (size == 0);
}
void Pop(double &num)
{
if (Empty())
{
cout << "error" << endl;
return;
}
Link* tmp = head;
num = head->number_;
head = head->next_;
delete(tmp);
--size;
}
void Pop(char &str)
{
if (Empty())
{
cout<<"error"<<endl;
return ;
}
Link* tmp = head;
str = head->buf_;
head = head->next_;
delete(tmp);
--size;
}
private:
int size;
struct Link* head;
};
inline void skipws(string &s, size_t &curr)
{
curr = s.find_first_not_of(" ", curr);//查找s里面第一个不是空格的位置
}
inline string scanner(string &s)
{
Stack stack;
string buf;
buf.clear();
size_t curr = 0;
char c;
while (curr < s.size())
{
skipws(s, curr);//跳过空格
switch (s[curr])
{
case '+':case '-':
if (stack.Empty())
{
stack.Push(s[curr]);//如果栈等于空就把当前字符入栈
}
else
{//如果栈不等于空
do
{
stack.Pop(c);//弹出一个字符传递给c
if ('(' == c)//如果弹出的栈里的值是左括号则入栈
stack.Push(c);
else
buf.push_back(c);//如果不是左括号则把弹出的栈的字符传给string
} while (!stack.Empty()&&'('!=c);//判断栈是否为空,或是否是左括号,如果不为空,或不等于左括号,则一直循环
stack.Push(s[curr]);
}
curr++;//位置自增
break;
case '*':case '/':case '('://如果是乘,除,左括号,就把字符入栈
stack.Push(s[curr]);
curr++;//位置自增
break;
case ')':
stack.Pop(c);//出栈,字符传入c
while ('('!=c)//只要不等于左括号
{
buf.push_back(c);//把c字符存入buf
stack.Pop(c);//继续出栈,把字符传给c进行判断
}
curr++;//位置自增
break;
case '0':case '1':case '2':case '3':case '4':case '5':
case '6':case '7':case '8':case '9':case '.':
buf.push_back(s[curr]);//当是数字字符时传给buf
curr++;//位置自增
if (!isdigit(s[curr]))//判断自增后是不是数字
buf.push_back(' ');//如果不是数字字符的话,就传一个空格,用来分隔每个数字
break;
default:
cout << "输入格式错误" << endl;
break;
}
}
while (!stack.Empty())//当栈不为空的时候把栈弹出来,把栈里的字符逐个传给buf
{
stack.Pop(c);
buf.push_back(c);
}
return buf;
}
inline void result(Stack& stack,string &s)
{
/*计算方法:遍历整理好的字符串,如果是数字就入栈,
如果遇到操作符+、-、*、/就弹出两个元素进行计算,并把计算结果入栈*/
size_t i = 0, t;
double u = 0 , r;
while (i<s.size())//i位置未到达s的尾部时进行循环
{
skipws(s, i);//跳过空格字符
switch (s[i])
{
case '+':
stack.Pop(u);//出栈存入u
stack.Pop(r);//出栈存入r
stack.Push(r + u);//相加存放到栈里
i++;
break;
case '-':
stack.Pop(u);//出栈存入u
stack.Pop(r);//出栈存入r
stack.Push(r - u);//相减存放到栈里(栈里头出栈后下面r减上面的u)
i++;
break;
case '*':
stack.Pop(u);//出栈存入u
stack.Pop(r);//出栈存入r
stack.Push(r * u);//相乘存放到栈里
i++;
break;
case '/':
stack.Pop(u);//出栈存入u
stack.Pop(r);//出栈存入r
if (u != 0)//除数不等于0
stack.Push(r / u);//相除存放到栈里
else
{
cout<<"除数为0,出错"<<endl;
stack.Push(r);
}
i++;
break;
case '0':case '1':case '2':case '3':case '4':case '5':
case '6':case '7':case '8':case '9':case '.':
u = stof(s.substr(i), &t);
//把s字符串从i位置开始截取到不是数字的字符,转换成doubel传递给u,并把截取的字符串位数传给t
i += t;//相加偏移到截取后的位置
stack.Push(u);//double入栈
break;
default:
break;
}
}
}
int main()
{
string buf = "1+(2-3)*4+10/5";
cout<<buf<<endl;
Stack stack;
size_t curr = 0;
double num;
while (true)
{
buf = scanner(buf);
cout << buf << endl;
result(stack, buf);
stack.Pop(num);//最后一个出栈存入的是结果,把值传给num
cout << num << endl;
cout<<"输入表达式"<<endl;
getline(cin, buf);
}
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <stack>
//逆波兰计算器用c++的自带stack
using namespace std;
void SkipWhite(string &s, size_t &curr)
{
curr = s.find_first_not_of(" ", curr);//查找第一个不是空格的地方,返回这个位置
}
string ProcesssStr(string& s)
{
stack<char> chs;
string buf;
buf.clear();
size_t curr = 0;
char tmp;
while (curr<s.size())
{
SkipWhite(s, curr);
if (s[curr] == '+' || s[curr] == '-')
{
if (chs.empty())
chs.push(s[curr]);
else
{
do
{
tmp = chs.top();
chs.pop();
if ('(' == tmp)
chs.push(tmp);
else
buf.push_back(tmp);
} while ((!chs.empty()) && '(' != tmp);
chs.push(s[curr]);
}
curr++;
}
else if ('*' == s[curr] || '/' == s[curr] || '(' == s[curr])
{
chs.push(s[curr]);
curr++;
}
else if (')' == s[curr])
{
tmp = chs.top();
chs.pop();
while ('(' != tmp)
{
buf.push_back(tmp);
tmp = chs.top();
chs.pop();
}
curr++;
}
else if (s[curr] >= '0'&&s[curr] < '9' || s[curr] == '.')
{
buf.push_back(s[curr]);
curr++;
if (!isdigit(s[curr]))
buf.push_back(' ');
}
else
cout << "输入格式错误" << endl;
}
while (!chs.empty())
{
tmp = chs.top();
buf.push_back(tmp);
chs.pop();
}
return buf;
}
inline double result(string &s)
{
stack<double> su;
/*计算方法:遍历整理好的字符串,如果是数字就入栈,
如果遇到操作符+、-、*、/就弹出两个元素进行计算,并把计算结果入栈*/
size_t curr = 0, t;
double u = 0, r;
while (curr < s.size())//curr位置未到达s的尾部时进行循环
{
SkipWhite(s, curr);//跳过空格字符
if (s[curr] == '+' || s[curr] == '-' || s[curr] == '*' || s[curr] == '/')
{
u = su.top();
su.pop();
r = su.top();
su.pop();
if (s[curr] == '+')
su.push(r + u);
else if (s[curr] == '-')
su.push(r - u);
else if (s[curr] == '*')
su.push(r * u);
else//除法
{
if (u != 0)
su.push(r / u);
else
{
cout<<"除数不能为0"<<endl;
su.push(r);
}
}
curr++;
}
else if (s[curr] >= '0'&&s[curr]<='9'||s[curr]=='.')
{
u = stof(s.substr(curr), &t);
su.push(u);
curr += t;
}
}
u = su.top();
su.pop();
return u;
}
int main()
{
string str = { "1+(2-3)*4+10/5" };
while (true)
{
str = ProcesssStr(str);
cout << str << endl;
cout << result(str) << endl;
cout << "输入表达式" << endl;
getline(cin, str);
}
system("pause");
return 0;
}
=====================================================================
=====================================================================
=====================================================================
表达式计算器4.3
=====================================================================
//保存为Node.h
//QQ108201645编写
/*1值语义是指对象的拷贝与原对象无关,拷贝之后与原对象脱离关系,彼此
互不影响,string,vector,map,c++内置类型都是值语义
一个类要实现深拷贝后才算是值语义
默认拷贝构造函数是浅拷贝,还是存在资源共享
2对象语义是面向对象意义下的对象,对象拷贝是禁止的,对象拷贝是允许的,复制
与被复制依然共享底层资源(基于对象编程->值语义,面向对象编程->对象语义)
值语生命周期容易控制,对象语义不容易控制
值语义对象通类对象的方式来使用,对象语义通常以指针或者引用方式来使用
对象语义一般通过智能指针来控制
auto_ptr 所有权独占,不能共享,但是可以转移
shared_ptr 所有权共享,内部维护了一个引用计数,+1,-1,减为0释放资源
weak_ptr 弱指针与shared_ptr配合使用,循环引用,获取的时候指针不会加1
scoped_ptr 所有权独占,不能共享,但是也不可以转移
配合智能指针就可以自动释放,RAII资源获取既初始化
将对象语义转换成值语义,在构造函数中获取析构中释放,值语义的析构是确定的*/
#ifndef _NODE_H_
#define _NODE_H_
#include <vector>
//我们是以对象语义的方式来使用,从形式上它能被拷贝
//不是深拷贝,仅仅是指针指向关系,当对象销毁时会去释放它的子代
class Noncopyable//这个类不是用来建立对象的
{
protected:
Noncopyable(){}
~Noncopyable(){}
private:
Noncopyable(const Noncopyable&);
const Noncopyable& operator=(const Noncopyable&);
};
//接口继承与实现继承
class Node : private Noncopyable//实现继承
{
public:
virtual double Calc()const=0;//计算的纯虚函数
virtual ~Node(){}
//基类指针指向派生类对象,经由基类指针释放对象的时候用纯虚函数才能调用派生类的析构
};
//NunberNode是一个具体类
class NumberNode:public Node
{
public:
NumberNode(double number):number_(number){}
double Calc()const;//
private:
const double number_;//数字节点有一个数据成员,一经初始化后就不会改变
};
//二元运算节点
//抽象类没有计算方法,只有它的派生类加减乘除运算节点才能计算
class BinaryNode:public Node
{
public:
BinaryNode(Node* left,Node* right)
:left_(left),right_(right){}//构造两个节点
~BinaryNode();//释放两个节点
protected://提升变成保护的成员,让派生类能调用
//左子节点右子节点一经初始化不能再改变的
//指针不能够再指向其它节点
Node* const left_;
Node* const right_;
};
//一元运算节点,没有实现Calculate方法,说明也是抽象类
class UnaryNode:public Node
{
public:
UnaryNode(Node* child)
:child_(child){}
~UnaryNode();//也应该定义它的析构函数
protected:
Node* const child_;//它只有一个孩子
};
//加法运算节点
class AddNode:public BinaryNode
{
public:
AddNode(Node* left,Node* right)
:BinaryNode(left,right){}//构造函数调用基类部分的构造函数
double Calc()const;//实现加法计算方法
};
//减法运算
class SubNode:public BinaryNode
{
public:
SubNode(Node* left,Node* right)
:BinaryNode(left,right){}
double Calc()const;
};
//乘法运算
class MultiplyNode:public BinaryNode
{
public:
MultiplyNode(Node* left,Node* right)
:BinaryNode(left,right){}
double Calc()const;
};
//除法运算并实现各自的计算方法
class DivideNode:public BinaryNode
{
public:
DivideNode(Node* left,Node* right)
:BinaryNode(left,right){}
double Calc()const;
};
//减号运算节点
class UMinusNode:public UnaryNode
{
public:
UMinusNode(Node* child)
:UnaryNode(child){}//调用基类的构造函数
double Calc()const;//实现计算方法
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Node.cpp
//QQ108201645编写
#include "Node.h"
#include <assert.h>
#include <cmath>
#include<iostream>
double NumberNode::Calc()const
{
return number_;//数字节点的计算方法就是等于数字节点本身
}
BinaryNode::~BinaryNode()
{
delete left_;
delete right_;
}
UnaryNode::~UnaryNode()
{
delete child_;//释放这个节点
}
//加法
double AddNode::Calc()const
{//加法节点等于它左节点计算的值加上右节点计算得到的值
return left_->Calc()+right_->Calc();
}
//减法
double SubNode::Calc()const
{
return left_->Calc()-right_->Calc();
}
//乘法
double MultiplyNode::Calc()const
{
return left_->Calc()*right_->Calc();
}
//除法
double DivideNode::Calc()const
{
//除数是否为0
double divisor=right_->Calc();
if (divisor != 0)
{
return left_->Calc()/divisor;
}
else
{
std::cout<<"Error : Divisor by zero"<<std::endl;
return HUGE_VAL;//的返回一个最大值(cmath文件头)
}
}
double UMinusNode::Calc()const
{
return - child_->Calc();//这个节点计算的值得到一个负号
}
―――――――――――――――――――――――――――――――――――――――
//保存为Scanner.h
//QQ108201645编写
//只负责扫描,并且登记当前的状态
#ifndef _SCANNER_H_
#define _SCANNER_H_
#include<string>
//表达式当前状态
enum EToken//枚举当前状态
{
TOKEN_END, //扫描完毕
TOKEN_ERROR, //错误状态
TOKEN_NUMBER, //扫描到一个数字
TOKEN_PLUS, //扫描到一个加号
TOKEN_MINUS, //扫描到一个减号
TOKEN_MULTIPLY, //扫描到一个乘号
TOKEN_DIVIDE, //扫描到一个除号
TOKEN_L_PARENTHESIS, //扫描到一个左括号
TOKEN_R_PARENTHESIS, //扫描到一个右括号
TOKEN_IDENTIFIER, //扫描到一个变量(标识符)
TOKEN_ASSIGN //
};
class Scanner
{
public:
Scanner(const std::string& buf);
void Accept();//扫描方法,一次扫描一个字符登记当前状态
double Number()const;//如果扫到数字,用这个返回数字
EToken Token()const;//返回当前状态
private:
void SkipWhite();//略过空白字符
const std::string buf_;//不能够改变的
unsigned int CurrentPos_;//当前扫描到的位置current
EToken token_;//返回状态
//把数字返回.以便Parser能够解析它
double number_;//返回数字
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Scanner.cpp
//QQ108201645编写
#include "Scanner.h"
#include<cctype>/*"ctype.h"*/
#include<iostream>
Scanner::Scanner(const std::string& buf):buf_(buf),CurrentPos_(0)
{
Accept();//调用构造时就要扫描了
}
double Scanner::Number()const
{
return number_;
}
EToken Scanner::Token()const
{
return token_;
}
void Scanner::SkipWhite()//忽略空白字符
{
while(isspace(buf_[CurrentPos_]))
++CurrentPos_;//当前位置是空白字符的话就++
}
void Scanner::Accept()//逐个字符扫描
{
SkipWhite();
switch(buf_[CurrentPos_])//判断当前字符是什么样的
{
case '+':
token_ = TOKEN_PLUS;
++CurrentPos_;
break;
case '-':
token_ = TOKEN_MINUS;
++CurrentPos_;
break;
case '*':
token_ = TOKEN_MULTIPLY;
++CurrentPos_;
break;
case '/':
token_ = TOKEN_DIVIDE;
++CurrentPos_;
break;
case '(':
token_ = TOKEN_L_PARENTHESIS;
++CurrentPos_;
break;
case ')':
token_ = TOKEN_R_PARENTHESIS;
++CurrentPos_;
break;
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
case '.':
token_ = TOKEN_NUMBER;
char* p;
//double strtod(const char*nptr,char** endptr)
//输入的字符串转成double并将指针的指针移到不是数字的位置
number_ = strtod(&buf_[CurrentPos_],&p);
/* std::cout<<number_<<std::endl;*/
CurrentPos_ = p-&buf_[0];//每次相减CurrentPos都会增加,因为p指针地址在变
break;
case '\0':case '\n':case '\r':case EOF:
token_ = TOKEN_END;
break;
default:
token_ = TOKEN_ERROR;
break;
}
}
―――――――――――――――――――――――――――――――――――――――
//保存为Parser.h
//QQ108201645编写
//根据扫描结果,进行解析,递归下降解析,直到生成一棵树
#ifndef _PARSER_H_
#define _PARSER_H_
class Scanner;//用前向声明可以不会使可执行文件增大
class Node;
enum STATUS//状态
{
STATUS_OK,
STATUS_ERROR,
STATUS_QUIT
};
class Parser
{
public:
Parser(Scanner &scanner);
void Parse();
Node* Expr();//表达式expression
Node* Term();//项term
Node* Factor();//因式factor
double Calculate()const;
private:
Scanner &scanner_;//持有一个引用对象
Node* tree_;//生成一棵表达式树
STATUS status_;//定义一个状态
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为parser.cpp
//QQ108201645编写
#include "Parser.h"
#include "Scanner.h"
#include "Node.h"
#include <cassert>
#include <iostream>
Parser::Parser(Scanner &scanner):scanner_(scanner),tree_(0)
{
}
void Parser::Parse()
{
tree_ = Expr();//解析它是否是一个表达式并返回一个node
}
//expression: =
//a,term+expression
//b,term-expression
//c,term
Node* Parser::Expr()
{
Node* node = Term();//解析它是否是一个项
//看看解析的状态
EToken token=scanner_.Token();//扫描器查看当前表达式的状态
if (token==TOKEN_PLUS)
{
scanner_.Accept();//接受加号,目的是为了扫描下一个字符
Node* nodeRight=Expr();//递归解析是否是一个表达式
node=new AddNode(node,nodeRight);//生成一个二元运算节点,并传递左右节点
}
else if (token==TOKEN_MINUS)
{
scanner_.Accept();
Node* nodeRight=Expr();
node=new SubNode(node,nodeRight);
}
//原先是右结合8-2+1=5改成如下左结合8-2+1=7
return node;
}
//term=
//a,factor * term
//b,factor / term
//c,factor
Node* Parser::Term()
{
Node* node = Factor();//解析它是否是一个因子
//看看解析的状态
EToken token=scanner_.Token();
if (token==TOKEN_MULTIPLY)
{
scanner_.Accept();
Node* nodeRight=Term();//递归调用,解析是不是一个项
node=new MultiplyNode(node,nodeRight);
}
else if (token==TOKEN_DIVIDE)
{
scanner_.Accept();
Node* nodeRight=Term();
node=new DivideNode(node,nodeRight);
}//原先是右结合8/2*4=1
return node;
}
//factor=
//a,number
//b,identifier变量
//c,- factor
//d,'('expression')'
Node* Parser::Factor()
{
Node* node;
EToken token=scanner_.Token();//扫描器查看当前扫描器的状态
if (token==TOKEN_L_PARENTHESIS)
{
scanner_.Accept();//接受左括号
node=Expr();//它应该是一个表达式
if (scanner_.Token()==TOKEN_R_PARENTHESIS)
{
scanner_.Accept();//接受右括号
}
else
{
status_ = STATUS_ERROR;
//Toto 抛出异常
std::cout<<"missing parenthesis"<<std::endl;
node=0;//等于空指针
}
}
else if (token==TOKEN_NUMBER)
{
node=new NumberNode(scanner_.Number());//新建一个数字节点,返回它的
scanner_.Accept();
}
else if (token==TOKEN_MINUS)
{
scanner_.Accept();//接受minus//a可寒pt
node=new UMinusNode(Factor());
}
else
{
status_ = STATUS_ERROR;
std::cout<<"Not a valid expression"<<std::endl;
node=0;
}
return node;
}
double Parser::Calculate()const
{
assert(tree_ != 0);
return tree_->Calc();
}
―――――――――――――――――――――――――――――――――――――――
//保存为main.cpp
//QQ108201645编写
#include <iostream>
#include<string>
#include "Scanner.h"
#include "Node.h"
#include "Parser.h"
using namespace std;
int main()
{
STATUS status = STATUS_OK;//默认
do
{
std::cout<<">";
std::string buf;
std::getline(std::cin,buf);//从标准cin获取输入,输入一行表达式放入buf
Scanner scanner(buf);//构造扫描器扫描这个表达式
//std::cout<<buf<<std::endl;
//Parser类不负责Scanner类对象的生命周期,是关联关系不是组合关系
Parser parser(scanner);//解析器解析扫描器状态,表达式,项,因子
parser.Parse();//解析方法
std::cout<<"输出:"<<parser.Calculate()<<endl;//计算表达式根节点的值
} while (status!=STATUS_QUIT);//status!=STATUS_QUIT既使解析错误也不跳出循环
}
//表达式expression
//trem
//factor因子
=====================================================================
=====================================================================
表达式计算器4.4
=====================================================================
//保存为Node.h
//QQ108201645编写
/*1值语义是指对象的拷贝与原对象无关,拷贝之后与原对象脱离关系,彼此
互不影响,string,vector,map,c++内置类型都是值语义
一个类要实现深拷贝后才算是值语义
默认拷贝构造函数是浅拷贝,还是存在资源共享
2对象语义是面向对象意义下的对象,对象拷贝是禁止的,对象拷贝是允许的,复制
与被复制依然共享底层资源(基于对象编程->值语义,面向对象编程->对象语义)
值语生命周期容易控制,对象语义不容易控制
值语义对象通类对象的方式来使用,对象语义通常以指针或者引用方式来使用
对象语义一般通过智能指针来控制
auto_ptr 所有权独占,不能共享,但是可以转移
shared_ptr 所有权共享,内部维护了一个引用计数,+1,-1,减为0释放资源
weak_ptr 弱指针与shared_ptr配合使用,循环引用,获取的时候指针不会加1
scoped_ptr 所有权独占,不能共享,但是也不可以转移
配合智能指针就可以自动释放,RAII资源获取既初始化
将对象语义转换成值语义,在构造函数中获取析构中释放,值语义的析构是确定的*/
#ifndef _NODE_H_
#define _NODE_H_
#include <vector>
//我们是以对象语义的方式来使用,从形式上它能被拷贝
//不是深拷贝,仅仅是指针指向关系,当对象销毁时会去释放它的子代
class Noncopyable//这个类不是用来建立对象的
{
protected:
Noncopyable(){}
~Noncopyable(){}
private:
Noncopyable(const Noncopyable&);
const Noncopyable& operator=(const Noncopyable&);
};
//接口继承与实现继承
class Node : private Noncopyable//实现继承
{
public:
virtual double Calc()const=0;//计算的纯虚函数
virtual ~Node(){}
//基类指针指向派生类对象,经由基类指针释放对象的时候用纯虚函数才能调用派生类的析构
};
//NunberNode是一个具体类
class NumberNode:public Node
{
public:
NumberNode(double number):number_(number){}
double Calc()const;//
private:
const double number_;//数字节点有一个数据成员,一经初始化后就不会改变
};
//二元运算节点
//抽象类没有计算方法,只有它的派生类加减乘除运算节点才能计算
class BinaryNode:public Node
{
public:
BinaryNode(Node* left,Node* right)
:left_(left),right_(right){}//构造两个节点
~BinaryNode();//释放两个节点
protected://提升变成保护的成员,让派生类能调用
//左子节点右子节点一经初始化不能再改变的
//指针不能够再指向其它节点
Node* const left_;
Node* const right_;
};
//一元运算节点,没有实现Calculate方法,说明也是抽象类
class UnaryNode:public Node
{
public:
UnaryNode(Node* child)
:child_(child){}
~UnaryNode();//也应该定义它的析构函数
protected:
Node* const child_;//它只有一个孩子
};
//加法运算节点
//class AddNode:public BinaryNode
//{
//public:
// AddNode(Node* left,Node* right)
// :BinaryNode(left,right){}//构造函数调用基类部分的构造函数
// double Calc()const;//实现加法计算方法
//};
////减法运算
//class SubNode:public BinaryNode
//{
//public:
// SubNode(Node* left,Node* right)
// :BinaryNode(left,right){}
// double Calc()const;
//};
////乘法运算
//class MultiplyNode:public BinaryNode
//{
//public:
// MultiplyNode(Node* left,Node* right)
// :BinaryNode(left,right){}
// double Calc()const;
//};
////除法运算并实现各自的计算方法
//class DivideNode:public BinaryNode
//{
//public:
// DivideNode(Node* left,Node* right)
// :BinaryNode(left,right){}
// double Calc()const;
//};
//减号运算节点
class UMinusNode:public UnaryNode
{
public:
UMinusNode(Node* child)
:UnaryNode(child){}//调用基类的构造函数
double Calc()const;//实现计算方法
};
//MultipleNode没有实现计算方法,所以还是抽象类
class MultipleNode:public Node
{
public:
MultipleNode(Node* node)//子节点的第一个节点
{
//-5-6+6当成是正的
AppendChild(node,true);
}
void AppendChild(Node* node,bool positive)//确定它的正负性
{
childs_.push_back(node);
positives_.push_back(positive);
}
~MultipleNode();//释放两个节点
protected:
//子代
std::vector<Node*> childs_;
std::vector<bool> positives_;//po se tvs
//乘积来说:正就是乘法,负就是除法
};
class SumNode:public MultipleNode
{
public:
SumNode(Node* node):MultipleNode(node){}
double Calc()const;
};
class ProductNode:public MultipleNode
{
public:
ProductNode(Node* node):MultipleNode(node){}
double Calc()const;
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Node.cpp
//QQ108201645编写
#include "Node.h"
#include <assert.h>
#include <cmath>
#include<iostream>
double NumberNode::Calc()const
{
return number_;//数字节点的计算方法就是等于数字节点本身
}
BinaryNode::~BinaryNode()
{
delete left_;
delete right_;
}
UnaryNode::~UnaryNode()
{
delete child_;//释放这个节点
}
//加法
//double AddNode::Calc()const
//{//加法节点等于它左节点计算的值加上右节点计算得到的值
// return left_->Calc()+right_->Calc();
//}
////减法
//double SubNode::Calc()const
//{
// return left_->Calc()-right_->Calc();
//}
////乘法
//double MultiplyNode::Calc()const
//{
// return left_->Calc()*right_->Calc();
//}
////除法
//double DivideNode::Calc()const
//{
// //除数是否为0
// double divisor=right_->Calc();
// if (divisor != 0)
// {
// return left_->Calc()/divisor;
// }
// else
// {
// std::cout<<"Error : Divisor by zero"<<std::endl;
// return HUGE_VAL;//的返回一个最大值(cmath文件头)
//
// }
//
//}
double UMinusNode::Calc()const
{
return - child_->Calc();//这个节点计算的值得到一个负号
}
MultipleNode::~MultipleNode()
{
std::vector<Node*>::const_iterator it;
for (it=childs_.begin();it!=childs_.end();++it)
{
delete *it;
}
}
//SumNode能处理加法运算也能处理减法运算
double SumNode::Calc()const
{
double result=0.0;
//计算子节点加起来
std::vector<Node*>::const_iterator childIt=childs_.begin();
std::vector<bool>::const_iterator positivesIt=positives_.begin();
for (;childIt != childs_.end();++childIt,++positivesIt)
{//取出*childIt里的节点进行计算
assert(positivesIt!=positives_.end());//是否遍历到结尾
double val=(*childIt)->Calc();
if (*positivesIt)
{
result+=val;
}
else
result-=val;
}
assert(positivesIt==positives_.end());
return result;
}
double ProductNode::Calc()const
{
double result=1.0;
//计算子节点加起来
std::vector<Node*>::const_iterator childIt=childs_.begin();
std::vector<bool>::const_iterator positivesIt=positives_.begin();
for (;childIt != childs_.end();++childIt,++positivesIt)
{//取出*childIt里的节点进行计算
assert(positivesIt!=positives_.end());//是否遍历到结尾
double val=(*childIt)->Calc();
if (*positivesIt)
{
result *= val;
}
else if (val != 0.0)
result /= val;
else
{
std::cout<<"Divisor by zero"<<std::endl;
return HUGE_VAL;
}
}
assert(positivesIt==positives_.end());
return result;
}
―――――――――――――――――――――――――――――――――――――――
//保存为Scanner.h
//QQ108201645编写
//只负责扫描,并且登记当前的状态
#ifndef _SCANNER_H_
#define _SCANNER_H_
#include<string>
//表达式当前状态
enum EToken//枚举当前状态
{
TOKEN_END, //扫描完毕
TOKEN_ERROR, //错误状态
TOKEN_NUMBER, //扫描到一个数字
TOKEN_PLUS, //扫描到一个加号
TOKEN_MINUS, //扫描到一个减号
TOKEN_MULTIPLY, //扫描到一个乘号
TOKEN_DIVIDE, //扫描到一个除号
TOKEN_L_PARENTHESIS, //扫描到一个左括号
TOKEN_R_PARENTHESIS, //扫描到一个右括号
TOKEN_IDENTIFIER, //扫描到一个变量(标识符)
TOKEN_ASSIGN //
};
class Scanner
{
public:
Scanner(const std::string& buf);
void Accept();//扫描方法,一次扫描一个字符登记当前状态
double Number()const;//如果扫到数字,用这个返回数字
EToken Token()const;//返回当前状态
private:
void SkipWhite();//略过空白字符
const std::string buf_;//不能够改变的
unsigned int CurrentPos_;//当前扫描到的位置current
EToken token_;//返回状态
//把数字返回.以便Parser能够解析它
double number_;//返回数字
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Scanner.cpp
//QQ108201645编写
#include "Scanner.h"
#include<cctype>/*"ctype.h"*/
#include<iostream>
Scanner::Scanner(const std::string& buf):buf_(buf),CurrentPos_(0)
{
Accept();//调用构造时就要扫描了
}
double Scanner::Number()const
{
return number_;
}
EToken Scanner::Token()const
{
return token_;
}
void Scanner::SkipWhite()//忽略空白字符
{
while(isspace(buf_[CurrentPos_]))
++CurrentPos_;//当前位置是空白字符的话就++
}
void Scanner::Accept()//逐个字符扫描
{
SkipWhite();
switch(buf_[CurrentPos_])//判断当前字符是什么样的
{
case '+':
token_ = TOKEN_PLUS;
++CurrentPos_;
break;
case '-':
token_ = TOKEN_MINUS;
++CurrentPos_;
break;
case '*':
token_ = TOKEN_MULTIPLY;
++CurrentPos_;
break;
case '/':
token_ = TOKEN_DIVIDE;
++CurrentPos_;
break;
case '(':
token_ = TOKEN_L_PARENTHESIS;
++CurrentPos_;
break;
case ')':
token_ = TOKEN_R_PARENTHESIS;
++CurrentPos_;
break;
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
case '.':
token_ = TOKEN_NUMBER;
char* p;
//double strtod(const char*nptr,char** endptr)
//输入的字符串转成double并将指针的指针移到不是数字的位置
number_ = strtod(&buf_[CurrentPos_],&p);
/* std::cout<<number_<<std::endl;*/
CurrentPos_ = p-&buf_[0];//每次相减CurrentPos都会增加,因为p指针地址在变
break;
case '\0':case '\n':case '\r':case EOF:
token_ = TOKEN_END;
break;
default:
token_ = TOKEN_ERROR;
break;
}
}
―――――――――――――――――――――――――――――――――――――――
//保存为Parser.h
//QQ108201645编写
//根据扫描结果,进行解析,递归下降解析,直到生成一棵树
#ifndef _PARSER_H_
#define _PARSER_H_
class Scanner;//用前向声明可以不会使可执行文件增大
class Node;
enum STATUS//状态
{
STATUS_OK,
STATUS_ERROR,
STATUS_QUIT
};
class Parser
{
public:
Parser(Scanner &scanner);
void Parse();
Node* Expr();//表达式expression
Node* Term();//项term
Node* Factor();//因式factor
double Calculate()const;
private:
Scanner &scanner_;//持有一个引用对象
Node* tree_;//生成一棵表达式树
STATUS status_;//定义一个状态
};
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Parser.cpp
//QQ108201645编写
#include "Parser.h"
#include "Scanner.h"
#include "Node.h"
#include <cassert>
#include <iostream>
Parser::Parser(Scanner &scanner):scanner_(scanner),tree_(0)
{
}
void Parser::Parse()
{
tree_ = Expr();//解析它是否是一个表达式并返回一个node
}
//expression: =
//a,term+expression
//b,term-expression
//c,term
Node* Parser::Expr()
{
Node* node = Term();//解析它是否是一个项
//看看解析的状态
EToken token=scanner_.Token();//扫描器查看当前表达式的状态
//if (token==TOKEN_PLUS)
//{
// scanner_.Accept();//接受加号,目的是为了扫描下一个字符
// Node* nodeRight=Expr();//递归解析是否是一个表达式
// node=new AddNode(node,nodeRight);//生成一个二元运算节点,并传递左右节点
//}
//else if (token==TOKEN_MINUS)
//{
// scanner_.Accept();
// Node* nodeRight=Expr();
// node=new SubNode(node,nodeRight);
//}
//原先是右结合8-2+1=5改成如下左结合8-2+1=7
if (token == TOKEN_PLUS||token == TOKEN_MINUS)
{
//Expr: = Term{('+'|'-')Term}
MultipleNode *multipleNode =new SumNode(node);//第一个节点实际是前面的项
do
{
scanner_.Accept();
Node* nextNode = Term();//看一下是否是一个项
multipleNode->AppendChild(nextNode,(token == TOKEN_PLUS));
//完了以后要更新token状态,需要解析下一个字符了,否则会导致后面失败
//如果是加法或减法是还会在这里面循环的
token=scanner_.Token();
} while (token == TOKEN_PLUS||token == TOKEN_MINUS);
node=multipleNode;
}
return node;
}
//term=
//a,factor * term
//b,factor / term
//c,factor
Node* Parser::Term()
{
Node* node = Factor();//解析它是否是一个因子
//看看解析的状态
EToken token=scanner_.Token();
//if (token==TOKEN_MULTIPLY)
//{
// scanner_.Accept();
// Node* nodeRight=Term();//递归调用,解析是不是一个项
// node=new MultiplyNode(node,nodeRight);
//}
//else if (token==TOKEN_DIVIDE)
//{
// scanner_.Accept();
// Node* nodeRight=Term();
// node=new DivideNode(node,nodeRight);
//}原先是右结合8/2*4=1
if (token == TOKEN_MULTIPLY||token == TOKEN_DIVIDE)
{
//Term: = Factor{('*'|'/')Factor}
MultipleNode *multipleNode =new ProductNode(node);//第一个节点实际是前面的项
do
{
scanner_.Accept();
Node* nextNode = Factor();//看一下是否是一个因式
multipleNode->AppendChild(nextNode,(token == TOKEN_MULTIPLY));
//完了以后要更新token状态,需要解析下一个字符了,否则会导致后面失败
//如果是加法或减法是还会在这里面循环的
token=scanner_.Token();
} while (token == TOKEN_MULTIPLY||token == TOKEN_DIVIDE);
node=multipleNode;
}
return node;
}
//factor=
//a,number
//b,identifier变量
//c,- factor
//d,'('expression')'
Node* Parser::Factor()
{
Node* node;
EToken token=scanner_.Token();//扫描器查看当前扫描器的状态
if (token==TOKEN_L_PARENTHESIS)
{
scanner_.Accept();//接受左括号
node=Expr();//它应该是一个表达式
if (scanner_.Token()==TOKEN_R_PARENTHESIS)
{
scanner_.Accept();//接受右括号
}
else
{
status_ = STATUS_ERROR;
//Toto 抛出异常
std::cout<<"missing parenthesis"<<std::endl;
node=0;//等于空指针
}
}
else if (token==TOKEN_NUMBER)
{
node=new NumberNode(scanner_.Number());//新建一个数字节点,返回它的
scanner_.Accept();
}
else if (token==TOKEN_MINUS)
{
scanner_.Accept();//接受minus//a可寒pt
node=new UMinusNode(Factor());
}
else
{
status_ = STATUS_ERROR;
std::cout<<"Not a valid expression"<<std::endl;
node=0;
}
return node;
}
double Parser::Calculate()const
{
assert(tree_ != 0);
return tree_->Calc();
}
―――――――――――――――――――――――――――――――――――――――
//保存为main.cpp
//QQ108201645编写
#include <iostream>
#include<string>
#include "Scanner.h"
#include "Node.h"
#include "Parser.h"
using namespace std;
int main()
{
STATUS status = STATUS_OK;//默认
do
{
std::cout<<">";
std::string buf;
std::getline(std::cin,buf);//从标准cin获取输入,输入一行表达式放入buf
Scanner scanner(buf);//构造扫描器扫描这个表达式
//std::cout<<buf<<std::endl;
//Parser类不负责Scanner类对象的生命周期,是关联关系不是组合关系
Parser parser(scanner);//解析器解析扫描器状态,表达式,项,因子
parser.Parse();//解析方法
std::cout<<"输出:"<<parser.Calculate()<<endl;//计算表达式根节点的值
} while (status!=STATUS_QUIT);//status!=STATUS_QUIT既使解析错误也不跳出循环
}
//表达式expression
//trem
//factor因子
=====================================================================
基于list,forward_list和string部分总结代码
=====================================================================
//QQ108201645编写
#include <iostream>
#include <list>
#include <forward_list>
#include <string>
#include <algorithm>
using namespace std;
size_t LocalList(list<int>& v,list<int>::const_iterator it)
{
size_t size = 0;
auto ix = v.begin();
for (; ix != v.end(); ++ix)
{
size++;
if (it==ix)
break;
}
return size;
}
void ShowList(list<int>& v)
{
cout << "List Size = " << v.size() << endl;
for (auto i : v)
cout << i << " ";
cout << endl;
}
void ShowFList(forward_list<int>& v)
{
cout << "List Size = " << list<int>(v.begin(),v.end()).size() << endl;
for (auto i : v)
cout << i << " ";
cout << endl;
}
//list查找值为3后并在该迭代器后插入该迭代器乘10的数值写入操作
void ListSearchInsert(list<int>& ilst)
{
auto ilstBeg = ilst.begin();
while (ilstBeg != ilst.end())
{
if (*ilstBeg == 3)
{
cout << "位置:" << LocalList(ilst, ilstBeg) << endl;//传入一个list对象与当前位置
auto it = ilstBeg;
++it;
ilstBeg = ilst.insert(it, *ilstBeg * 10);//会根据当前的it迭代器位置返回
cout << "插入后位置:" << LocalList(ilst, ilstBeg) << endl;
++ilstBeg;
}
else
++ilstBeg;
}
}
//删除值为30的元素
void ListDelVal(list<int> &ilst)
{
auto beg = ilst.begin();
while (beg!=ilst.end())
{
if (*beg == 30)
beg = ilst.erase(beg);
else
++beg;
}
}
void FListSearchInsert(forward_list<int>& filst)
{
auto curr = filst.begin();
while (curr!=filst.end())
{
if (*curr == 3)
{
curr = filst.insert_after(curr, *curr * 10);//在当前curr的位置下一个元素开始添加元素写入值是3*10
curr++;//跳过当前与添加元素的位置
}
else
{
curr++;
}
}
}
void FListDelVal(forward_list<int>& filst)
{
auto curr = filst.begin(), prev = filst.before_begin();
while (curr != filst.end())
{
if (*curr == 30)
{
curr = filst.erase_after(prev);//在当前prev的位置下一个下一个元素开始删除
}
else
{
prev = curr;//等于当前元素
curr++;//自增后,是下一个元素
}
}
}
//string替换里头字符串方法1
void Replace1(string& s, string& oldVal, string& newVal)
{
string::iterator iter = s.begin();
while (iter!=s.end())
{
string::iterator iter1 = iter,
iter2 = oldVal.begin();
while (iter2!=oldVal.end()&&*iter1==*iter2)
{
iter1++;
iter2++;
if (iter1==s.end())
break;
}
if (iter2 == oldVal.end())
{
iter = s.erase(iter, iter1);
if (newVal.size())
{
iter2 = newVal.end();
do
{
--iter2;
iter = s.insert(iter, *iter2);
} while (iter2 != newVal.begin());
}
iter += newVal.size();
}
else
iter++;
}
}
//string替换里头字符串方法2
void Replace2(string& s, string& oldVal, string& newVal)
{
string::size_type p = 0;
while ((p=s.find(oldVal,p))!=string::npos)
{
s.replace(p, oldVal.size(), newVal);//从p开始的位置把oldVal大小的字符串,替换成newVal字符串
p += newVal.size();//移动到添加的内容之后的下标位置
}
}
int main()
{
list<int> ilst{ 1,2,3,4,5 };
//调用list查找插入操作的函数
ListSearchInsert(ilst);
ShowList(ilst);//输出内容:1 2 3 30 4 5
//删除值为30的元素的函数
ListDelVal(ilst);
ShowList(ilst);//输出内容: 1 2 3 4 5
forward_list<int> flist{ 1,2,3,4,5,6 };
//调用forward_list查找值为3后并在该迭代器后插入该迭代器乘10的数值写入操作的函数
FListSearchInsert(flist);
ShowFList(flist);//输出内容:1 2 3 30 4 5 6
//删除值为30的元素的函数
FListDelVal(flist);
ShowFList(flist);//输出内容:1 2 3 4 5 6
string s = { "hello world 123 456" };
string oldVal="world",newVal = { "xxx" };
//调用方法1,用newVal替换oldVal
Replace1(s, oldVal, newVal);
cout << s << endl;//输出内容: hello xxx 123 456
Replace2(s, newVal, oldVal);
cout << s << endl;//输出内容: hello world 123 456
system("pause");
return 0;
}
=====================================================================
string单个字符删除与替换操作
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
void Replace(string& s,string& oldVal, char newVal)
{
string::size_type pos = 0;
while ((pos =s.find_first_of(oldVal, pos))!=string::npos)
{
s.erase(pos,1);//删除大小==1,如果只有pos的话,默认是从pos位置开始全部删除
s.insert(s.begin()+ pos, newVal);
}
}
void Replace(string& s, string& oldVal, string newVal)
{
string::size_type pos = 0;
auto it = newVal.begin();
while ((pos=s.find_first_of(oldVal,pos))!=string::npos)
{
s.erase(pos, 1);
s.insert(s.begin()+pos, *it);
it++;
if (it == newVal.end())
it = newVal.begin();
}
}
void DelStr(string& s, string& oldVal)
{
size_t pos = 0;
while ((pos=s.find_first_of(oldVal,pos))!=string::npos)
{
s.erase(pos, 1);//仅仅是删除
}
}
void Insert(string& s, string& oldVal, string& newVal)
{
size_t pos = 0;
auto it = newVal.begin();
while ((pos = s.find_first_of(oldVal, pos)) != string::npos)
{
s.insert(pos+1, 1,*it);//仅仅是插入一个字符
pos+=2;
++it;
if (it == newVal.end())
it = newVal.begin();
}
}
int main()
{
string s = { "a`b~c.d/e:f.g'f.g/h/i" };
string s2 = s;
string oldVal = { "`~!@#$%^&*()_+,./\t:;'" }, newVal = { "0123456789" };
Replace(s, oldVal, ' ');//把s在oldVal中出现的字符都替换成空格
cout << s << endl;//输出内容a b c d e f g f g h i
Replace(s2, oldVal, newVal);//把s2在oldVal中出现的字符都替换成newVal字符,并依次递进
cout << s2 << endl;//输出内容a0b1c2d3e4f5g6f7g8h9i
s2 = s;//s2 =a b c d e f g f g h i
string oldVal1 = " \t/";
DelStr(s, oldVal1);//当是空格时就删除
cout << s << endl;//输出内容abcdefgfghi
Insert(s2, oldVal1, newVal);//当是空格的字符时就在空格后插入“0到9”的字符
cout << s2 << endl;//输出内容a 0b 1c 2d 3e 4f 5g 6f 7g 8h 9i
system("pause");
return 0;
}
=====================================================================
#include<iostream>
#include<string>
using namespace std;
int main()
{
//单个字符删除操作
string s = "a b c";
string oldVal = " \t";//要删除的字符
string::size_type pos = 0;
while ((pos = s.find_first_of(oldVal, pos)) != string::npos)
{
s.erase(pos, 1);
}
cout << s << endl;//输出内容abc
//单个字符替换操作
string s2 = "a b c d e";
pos = 0;
while ((pos = s2.find_first_of(oldVal, pos)) != string::npos)
{
/*s2.erase(pos, 1);
s2.insert(pos, 1, '/');
*/
s2.replace(s2.begin() + pos, s2.begin() + pos + 1, "/");//等价于s2.replace(pos,1,"/");
}
cout << s2 << endl;//输出内容a/b/c/d/e
system("pause");
return 0;
}
=====================================================================
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
using namespace std;
void Show(vector<int>& v)
{
cout << v.size() << endl;
for (auto i : v)
cout << i << " ";
cout << endl;
}
int main()
{
map<char, int> clmap;
string s = "000hello18300001234";
string s1(s);
sort(s.begin(), s.end());//对字符进行排序
cout << s << endl;
s.erase(unique(s.begin(), s.end()), s.end());//删除相同元素
cout << s << endl;
cout << s1 << endl;
for (string::iterator it = s1.begin(); it != s1.end(); ++it)
{
for (string::iterator it1 = it; //从当前位置开始遍历
it1 != s1.end(); )
{
auto t = it1 + 1;
if (*it == *it1&&it != it1)//元素的值相同。且地址不同
it1 = s1.erase(it1, t);//删除从it1开始到t迭代器结束的一个字符
else
++it1;
}
}
cout << s1 << endl;
#ifndef DECLARATION//把n去掉等于注释下面代码
//vector删除重复元素(无序)
vector<int> ivec{ 1,2,3,1,2,3 };
auto it = ivec.begin() + 1;
vector<int>::iterator it1;
while (it != ivec.end())
{
it1 = find(ivec.begin(), it, *it);//查找从begin()开始的到it结束的位置。之前的如果与当前相同
if (it != it1)//且位置不同就执行删除
it = ivec.erase(it);
else
++it;//否则自增位置
}
Show(ivec);//输出内容1 2 3
#endif
system("pause");
return 0;
}
=====================================================================