// 第十章标准模板库 //.cpp : 定义控制台应用程序的入口点。 //STL提供的功能 //容器的定义以及如何创建和使用容器 //迭代器的定义以及如休在容器中使用迭代器 //STL可用的算法类型以及如何应用较常用的算法 //函数对像的定义以及如何在STL中使用函数对像 //如何使用支持C++/CLI类类型的STL版本 //10.1 标准模板库的定义 //STL包含六种组件: 容器, 容器适配器 迭代器 算法 函数对像和函数适配器 //vector<string> strings; //vector<doble> data; //STL容器类的模板在标准头文件中定义 //<vector> vector<T>容器表示一个在必要时可动增加容量的数组,只能在矢量容器的末尾添加新元素 //<deque> deque<T>容器实现一个又端队列,它等价于一个矢量,不过增加了向容器开头添加元素 //<list> list<T>是一个双向链表 //<map> <map<K,T>是一个关链容器,用关联链(类型为K)存储确定链/对像对所有位置的第个对像(类型为T),映射中的第个键的值必须唯一一 //这个头文件也定义multimap<K,T>容器, //基中键/对像对中的键不需要唯一 //<set> set<T>容器是一个映射,基中名对像作为自身的键,集合中的所有对像必须唯一,使用对像作为自身的键的一个后果是无法在集合中修改对像,要修改对像,必须先删除它,然后插入修改后的版本 //这个头文个也定在multimap<T>容器,它与集合容器相似,只是基中的条目不需要唯一 //<bitset> 定义表示固定位数的bitset<T>类模板,它通常用来存储表示一组状态或条件的标记(flag); //MVC++也包括定义hash_map<K, T>和hash_set<K,T>的头文件<hash_map>和<hash_set> ,它们是map<K,T>和set<K,T>容器的非标准变体,因为它们是在stdext命名空间中定义的(而不是在std中),标准映射和集合容器用一个排序机制定位条目,而非标准hash_map和hash_set容器用巧用列机制定位条目 //10.1.2 容器适配器 //<queue> 默认情况下,queue<T>容器由deque<T>容器中的适配器定义,不过我们可以用list<T>容器定它,我们只能访问队列中第一个和最后一个元素,而且我们只能从后面添加元素,从前面删除元素,因此queue<T>容器的运行或多或少就像我们在咖啡店排队一样 //这个头文件还定义了一个priority_queue<T>容器,它是一个排列它所包含的元素顺序的队列,因此最大的元素总是最前面,只有前端的元素可以被访问或删除,默认情况下,优化级队列由vector<T>中的适配器定义,不过我们可以用deque<T>作为基础容器 //<stack> 默认情况下,堆栈容器用户deque<T>容器中的适配器定义,不过我们可以用vector<T>和list<T>容器定义它,堆栈是一种先进行出的容器 //因此添加或删除元素总是发生在顶部,我们只能访问顶部元素 //10.1.3 迭代器 //迭代器的行为与指针相似,它对于访问除容器适配器定义的内容之外的所有STL容器的内容非常重要.容器适配器不支持迭代器 //输入与输出迭代器: //这些这些迭代器读写对像的序列,可能仅使用一次,为了读或写第二次,必须获得一个新的迭代器,我们可以在迭代器上进行下次运算 //++iter或--iter //*iter //对于解引用运算,允许在输入迭代器的情况下进行只读访问,在输出迭代器的情况下进行只写访问 //前向迭代器: //前向迭代器结合了输入和输入迭代器的功能,因此我们可以对它们应用上面显示的运算,而且可以用他们进行访问和存储操作, //也可以重用向前迭代器向前的访向历一组对像,我们想foreach几次就foreach就几次 //双向迭代器: //双向迭代器提供了与前向迭代器相同的功能,此外,还允许的--iter和iter--运算,这意味们可以后向也可以前向foreach对像序列 //随机访问迭代器: //随机访问迭代器具有与双向迭代器相同的功能,不过还允许下列运算 /*iter+n 或iter-n iter+=n 或 iter-=n iter1 - iter2 iter1 < iter2 或 iter1 > iter2 iter1 <= iter2 或 iter1 >= iter2 iter[n] 将一个迭代器递增或递减任意值n的能力允许随机访问对像的庥合,最后一个使用[]运算符的运算等价于*(iter+n) */ //10.1.4 算法 //算法是操作迭代器提供的一组对像的STL函数模板,因为对像是由迭代器提供的,所以算法不需要知道要处理的对像的来源 //算法在两个标准头文件中定义:<algorithm>和<numeric>头文件 //10.1.5 函数对像 //函数对像是重载()运算符的类类型的对像,即该类实现operator()()函数,函数对像类型在STL定义为模板,因此我们可以创建一个函数对像,其中overloaded()运算符函数使用我们的对像类型 //很多算法使用函数对像来指定要进行的二元运算,或者指定谓词来确定要如何或是否进行特定的运算 //谓语是一个返回bool类型的值的函数,因为函数对像是一种类型的对像,实现operator()()成员函数并返回bool类型的值,所以函数对像也是谓词 //谓词有两个版本,分别是需要两个操作数的二无谓词和需要一个操作数的一无谓词,例如: //小于或等于这样的比较以及AND和OR这样的逻辑运算作为二元谓词(它们是函数对像的成员) //逻辑运算NOT作为一元谓词(此谓词也是函数对像的成员) //函数对像模板在<functional>头文件中定义,必要时我们也可以定义自己的函数对像, //1.01.6 函数适配器 //函数适配器是允许合并函数对像以产生一个更复杂的函数对像的函数模板, //10.2 STL容器范围 //10.3 序列容器 //evctor<T> 头文件:<vector> 创建一个表示存储T类型对像的动态数组类 //list<T> 头文件:<list> 创建一个存储T类型的一个链表类 //deque<T> 头文件:<deque> 创建一个存储T类型对像的双端队列的类 //10.3.1 创建矢量容器 //vector<int> mydata; /*#include "stdafx.h" #include <iostream> #include <string> #include <vector> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { //创矢量容器最简单的方工 //vector<int> mydata; //push_back()函数向矢量的末尾添加一个新的元素 //mydata.push_back(1); //vector<double> mydouble(100); //创建一个包含有100个元素的矢量, 默认值为0 //将第二个元素赋值 //mydouble[2] = 2.00; //vector<int> mydata(100, -1); //将矢量中的元素初始化为不同的值 //如果在创建容器时不想创建元素,则可以通过调用它的reserve()函数来创建 //vector<int> mydata; //mydata.reserve(100); //reserve()函数的实参是要容红领巾的最小元素个数,如果实参小于矢量的当前容量,那么调用reserve()就没有用, //我们可以用外部数组中的元素作为初值来创建矢量图 //double data[] = {1.5, 2.5, 3.5, 4.5, 5.5, 6.5}; //vector<double> mydata(data, data+4); //data+4也就是长度,将四个元素插入其中 //vector<double> values<mydata.begin(), mydata.end()); //begin()和end()函数返回的迭代器类型是vector<T>::iterator,基中T是矢量存储的对像类型 //vector<double> values<mydata.begin()+2, mydata.end()-1); //10.3.2 矢量容器的容量和大小 //现在我们来解释矢量容器的容量的(capacity)与大小(size)之间的区别 //容量是在不分配更多内存的情况下容器当前可以容纳的最大对象的数目, //大小是实际存储在容器中的对像数目,因此大小不能大于容量 double data[] = {1.5, 2.5, 3.5, 4.5, 5.5, 6.5}; vector<double> mydata(data, data+4); cout<<endl<<"the mydata is 容器的容量是:"<<mydata.capacity()<<endl; cout<<"the mydata is 容器的大小是:"<<mydata.size()<<endl; //这两个的返回类型都是vector<T>::size_type; //保存容器的大小和容器的容量 vector<double>::size_type cap = mydata.capacity(); cout<<"cap::"<<cap<<endl; //注意:STL的Microsoft Visual C++库实现定义vector<T>::size_type类型,因为size_t.size_t是无符号整型,它也是sizeof运算符的结果以及new运算符返顺的值的类型 if(mydata.empty()){ cout<<"mydata empty"<<endl; }else{ cout<<"mydata not empty"<<endl; } //当前容器是否为空 //max_size()函数可能不常用到,但是调用矢量的该函数可以发现元素的最大可能数目 vector<int> strings; cout<<"<Maximum length of strings vector:"<<strings.max_size()<<endl; //调用矢量的resize()函数可以修改矢量的大小,这个函数可以增加或减少矢量的大小,如果我们指定一个当前大小的新大小,那么从矢量的末尾删除足够的元素,使它减小到新的大小 //如果新大小大于旧大小,那么会向矢量的末尾添加新元素将长度增加到新的大小 vector<int> values(5,66); //创建5个66数值的容器 values.resize(7,88); //重新设置为长度为7个,后面两个元素是88的数组 values.resize(10); //长度为10,后面两个添加上去的元素值为0 values.resize(4); //只保留前面4个的值,都是66 return 0; }*/ /* #include "stdafx.h" #include <iostream> #include <string> #include <vector> using namespace std; template<class T> void listInfo(vector<T> &v) { cout<<"Container capactity:"<<v.capacity()<<endl; cout<<"size:"<<v.size()<<endl; } int _tmain(int argc, _TCHAR* argv[]) { vector<double> data; listInfo(data); cout<<"重新设置data的长度为100:"<<endl; data.reserve(100); listInfo(data); vector<int> numbers(10, -1); cout<<"The initial values are:"; for(vector<int>::size_type i = 0; i<numbers.size(); i++){ cout<<" "<<numbers[i]; } cout<<endl; for(vector<int>::iterator iter = numbers.begin(); iter<numbers.end(); iter++){ cout<<" "<<*iter; } vector<int>::size_type oldC = numbers.capacity(); //容量 vector<int>::size_type newC = oldC; listInfo(numbers); for(int i=0; i<1000; i++) { numbers.push_back(2*i); newC = numbers.capacity(); //新的容量 if(oldC < newC){ oldC = newC; listInfo(numbers); } } } */ //10.3.3 访问矢量中的元素 //for(vector<int>::size_type i=0; i<numbres.size(); i++) // cout<<" "<<numbres.at(i); //如果用超出合法范围的下标运算符进行下标,结果就不确定,如果用at()函数进行下标,那么会抛出out_of_range类型的异常 //访问矢量容器中的第一个或最后一个元素,可以分别调用front()或者back()函数 //const int& firstvalue = numbers.front(); //int& lastvalue = numbers.back(); //10.3.4 在矢量中插入和删除元素 //pop_back() //numbers.pop_back() 删除矢量numbers中的最后一个元素,并将大小减去一,如果该矢量中没有元素,那么调用pop_bakc()就没有用 //clear() numbers.clear() 删除numbers中的所有元素,因此大小将变成0,当然,容量仍然保持不变 //insert()函数在矢量中的任何地方插入一个或多个元素,不过此操作会以线性时间执行,这意味着时间会根据容器中的元素数目按比例增加 //insert()函数的最简版本在矢量中的特定位置插入一个新元素, //其中第一个实参是指要插入元素的位置的迭代器,第二个实参是要插入的元素 //vector<int> vec(5, 999); //vec.insert(vec.begin()+1, 88); //在当前第一个元素后面加88 //vec.insert(vec.begin()+3, 3, 77); //insert()函数还有另一个在给定位置插入一个元素序列的版本,第一个实参是指向要插入第一个元素的位置的迭代器,第二个实参和第三个实参是指定要插入的某个来源中的元素范围的迭代器 //vector<int> newvec(5,33); //newvec.insert(newvec.begin()+1, vec.begin()+1, vec.begin()+5); //erase()函数可以删除矢量中任何位置的一个或多个元素,但是它也是线性时间函数,通常会比较慢 //newrec.erase(newvec.end()-2); //为了删除多个元素,需提供两个迭代器实参来指定间隔 //newvec.erase(newvec.begin()+1, newvec.begin()+4); //正如前面所提到的,erase()和insert()操作比较慢,因此在使用矢量时要慎用它们,如果我们发现在应用程序中经常要用到它们,选择list<T>容器可能会更好 //swap()函数用来交换两个矢量的内容,前提当然是假定这两个矢量中的元素的类型相同, //vector<int> first(5, 77); //vector<int> second(8, -1); //first.swap(second); //矢量的容量和内容交换了,大小当然也交换了 //assign()函数用来用别一个序列替换一个矢量中的全部内容,下面是如何用别一个矢量中的序列替换一个矢量中的内容 //vector<double> values; //for(int i=1; i<=50; i++) //{ // values.push_back(2.5*i); //} //vector<double> new newdata(5,3.5); //newdata.assign(values.begin()+1, values.end()-1); //它用来删除newdata中的所有元素,然后插入values中除第一个和第二个元素外的所有元素的副本, //我们通过两个迭代器指定要插入的新序列,第一个迭代器指向要插入的第一个元素 //第二个迭代器指向要插入的最后一个元素的下一个位置 //newdata.assign(30, 99.5); //第一个实参是替换序列中的元素个数, //第二个实参是要使用的元素,这个语句将导致newdata的内容被删除并用30个元素替换,每个元素的值都是99.5 //10.3.5 在矢量中存储类对像 /*class T { public: T(); T(const T& t); ~T(); T& operator=(const T& t); }*/ /* class Person { public: Person(char* first="John", char* second = "Doe") { size_t length = strlen(first)+1; firstname = new char[length]; strcpy_s(firstname, length, first); length = strlen(second)+1; secondname = new char[length]; strcpy_s(secondname, length, second); } Person(const Person& p) { size_t length = strlen(p.firstname)+1; firstname = new char[length]; strcpy_s(firstname, length, p.firstname); length = strlen(p.secondname)+1; secondname = new char[length]; strcpy_s(secondname, length, p.secondname); } ~Person(){ delete[] firstname; delete[] secondname; } Person& operator=(const Person& p) { if(&p == this){ return *this; } delete[] firstname; delete[] secondname; size_t length = strlen(p.firstname)+1; firstname = new char[length]; strcpy_s(firstname, length, p.firstname); length = strlen(p.secondname)+1; secondname = new char[length]; strcpy_s(secondname, length, p.secondname); } bool operator<(const Person& p)const { int result = strcmp(secondname, p.secondname); //取得比较结果,等于返回0, 小于时返回小于0的值,大于返回大于0的值 //小于或者等于,并且姓也得小于 if(result < 0 || result == 0 && strcmp(firstname, p.firstname) < 0) return true; return false; } void ShowPerson() const { cout<<firstname<<" "<<secondname<<endl; } private: char* firstname; char* secondname; }; using namespace std; int _tmain(int argc, _TCHAR* argv[]) { return 0; }*/ /* #include "stdafx.h" #include <iostream> #include <vector> #include "Person.h" using namespace std; //int _tmain(int argc, _TCHAR* argv[]) int main() { vector<Person> people; const size_t maxLength = 5; char firstname[maxLength]; char secondname[maxLength]; while(true) { cout<<"请车入姓(按0退出):"; cin.getline(firstname, maxLength, '\n'); if(strlen(firstname) == 0){ break; } cout<<"请输入名:"; cin.getline(secondname, maxLength, '\n'); people.push_back(Person(firstname, secondname)); //cout<<"退出请输入0, 再录入请按1"; } cout<<endl; cout<<"开始循环输出"<<endl; vector<Person>::iterator iter = people.begin(); while(iter != people.end()){ iter++->ShowPerson(); } return 0; } */ //10.3.6 排序矢量元素 //在<algorithm>头文件中定义的sort()函数模板会排序两个随机访问迭代器所指向的对像序列 //这两个迭代器分别指向序列中的第一个对像和最后一个对像的下一个位置, //注意:随机访问迭代器至关重要,容量较小的迭代器会不够用,sort()函数模板用<运算符来排列元素的顺序 //因此我们可以用sort()模板来排序提供随机访问迭代器的任何容器的内容,只要它包含的对像可以用<运算符进行比较 //sort(people.begin(), people.end()); /*#include "stdafx.h" #include <iostream> #include <vector> #include <algorithm> #include <functional> #include "Person.h" using namespace std; //int _tmain(int argc, _TCHAR* argv[]) int main() { vector<Person> people; const size_t maxLength = 5; char firstname[maxLength]; char secondname[maxLength]; while(true) { cout<<"请车入姓(按0退出):"; cin.getline(firstname, maxLength, '\n'); if(strlen(firstname) == 0){ break; } cout<<"请输入名:"; cin.getline(secondname, maxLength, '\n'); people.push_back(Person(firstname, secondname)); //cout<<"退出请输入0, 再录入请按1"; } cout<<endl; cout<<"开始循环输出"<<endl; vector<Person>::iterator iter = people.begin(); while(iter != people.end()){ iter++->ShowPerson(); } sort(people.begin(), people.end()); cout<<"排序后开始循环输出"<<endl; //vector<Person>::iterator iter = people.begin(); vector<Person>::iterator iter1 = people.begin(); while(iter1 != people.end()){ iter1++->ShowPerson(); } const size_t max = 100; int data[max]; int value = 0; size_t count = 0; for(size_t i = 0; i<max; i++){ cin>>value; if(value == 0){ break; } data[count++] = value; } //开始使用sort排序 //sort (data, data+count); sort(data, data+count, greater<int>()); for(size_t i = 0; i<max; i++){ cout<<"i:"<<i<<" "<<data[i]<<endl; } //可以对数组用sort()模板函数,唯一的要求是运算符应当使用存储在数组中的元素的类型, //注意,标记要排序的元素序列的指针必须仍然指向最后一个元素的下一个位置 return 0; }*/ //10.3.7 排序矢量中的指针 //与其他容器一样,矢量容器为添加到其中的对像创建一个副本,在大多数情况下这是极大的优点,但是在有些情况这个功能可能非常不访便 //如果我们的对像比较大,向容器中添加对像时复制它们的开销会相当大,这种场合下,在容器中存储对像的指针并在外部管理对象比存储对像本身更好 /*#include "stdafx.h" #include <iostream> #include <vector> #include <algorithm> #include <functional> #include "Person.h" using namespace std; //int _tmain(int argc, _TCHAR* argv[]) int main() { vector<Person*> people; //定义一个容器people,但元素为Person的指针 const size_t maxLength = 50; char firstname[maxLength]; char secondname[maxLength]; while(true){ cout<<"请车入姓(按0退出):"; cin.getline(firstname, maxLength, '\n'); if(strlen(firstname) == 0){ break; } cout<<"请输入名:"; cin.getline(secondname, maxLength, '\n'); //people.push_back(Person(firstname, secondname)); people.push_back(new Person(firstname, secondname)); //每个Person对像现在在堆上创建,地址传递给该矢量push_back()函数 //在容器中存储对像的地址要小心一些,如果在堆栈上创建对像,那么当函数退出时这些对像被销毁,存储的指针会变成无效指针 //如果用new运算符在堆上创建的对像,那么只有当用delete删除它们时对像才被销毁 } cout<<endl; vector<Person*>::iterator iter = people.begin(); while(iter != people.end()){ (*(iter++))->ShowPerson(); } //删除所有容器内容 iter = people.begin(); while(iter != people.end()){ delete *(iter++); } people.clear(); return 0; }*/ //10.3.8 双端队列容器 //双端队列容器模板deque<T>是在<deque>头文件中定义的,双端队列容器非常类似于矢量, //其功能与矢量容器相同,并且包括同样的函数成员,但是我们可以在序列的开头和结尾有效的添加和删除元素 ///deque<Person> people; ///向容器的前端添加元素的函数是push_front() pop_front删除第一个元素 //people.push_front(Person(firstname, secondname)); //deque<string> strings; //deque<int> items(50); //default value; //??什么意思 //deque<double> values(5, 0.5); //deque<int> data(items.begin(), items.end()); //与矢量相比它有一个缺点,由于它提供了额外的能力,双端队列的内存管理比矢量复杂 //因此它会稍微慢一些 //除非确实需要向容器的前端添加元素,否则矢量是更好的选择 /*#include "stdafx.h" #include <iostream> #include <algorithm> #include "Person.h" #include <deque> #include <numeric> using namespace std; //int _tmain(int argc, _TCHAR* argv[]) int main() { deque<int> data; //定义双端队列 deque<int>::iterator iter; //存储一个迭代器,是一个指针 deque<int>::reverse_iterator riter; //存储一个反向迭代器 cout<<"请输入数值到队列中,0退出:"; int value = 0; while(cin>>value, value !=0){ data.push_front(value); //添加到头部 } cout<<endl; cout<<"你输入的数值列表是"<<endl; for(iter = data.begin(); iter!=data.end(); iter++){ cout<<*iter<<" "; //迭代器是一个指针 } cout<<endl; cout<<"对列表进行从后到前输出"<<endl; for(riter = data.rbegin(); riter != data.rend(); riter++){ cout<<*riter<<" "; //迭代器是一个指针 } //rbegin()函数返回一个指向最后一个元素的迭代器 //rend()函数返回一个指向第一个元素前面的一个位置的迭代器 cout<<endl; cout<<"对列表进行排序以后处理:"<<endl; sort(data.rbegin(), data.rend()); //这里是返向的迭代器,咱也用++呢 for(iter = data.begin(); iter != data.end(); iter++){ cout<<*iter<<" "; } cout<<endl; cout<<"accumulate(data.begin(), data.end(), 0):"<<accumulate(data.begin(), data.end(), 0)<<endl; //accumulate在文件头<numeric>中 //这个会累计前两个迭代器实参标识的元素序列的和,第三个实参指下和的一个初值 //并且其类武装必须与序列中的元素的类型相同,即使序列的和是空值,也要提供一个初值 //以确保我们总是得到有意义的结果,accumulate()函数返回运算结果, //我们可以对任何的数值类型的值序列应用accumulate()函数 return 0; }*/ //10.3.9 使用列表容器 //在<list>头文件中定义的List<T>容器模板实现一个双向链表,与矢量或双端队列相比,列表容器最大的优点是可以在固定时间从序列的任意位置插入或删除元素 //列表的容器的构造函数范围类似于矢量或双端队列 //list<string> names; //list<string< names(20); 长度为20 //list<double> values(50, 2.17828); //list<double> samples(++values.begin(), --values.end()); //与其他序列容器一样,我们可以调用列表的size()成员函数来查看它的无素数目前,也可以通过调用列表的resize()函数来修改它的元素数目 //如果resize()的实参小于列表中的元素数目就从末尾删除元素,如果实参较大,就用存储的元素类型的默认构造函数添加元素 //1 向列表中添加元素 //提供了push_front() / push_back() / 还有第三个版本的insert()函数, //list<int> data(20, 1); //data.insert(++data.begin(), 77); //第一个实参是在插入位置指定的迭代器,第二个实参是要插入的元素, //在指定位置插入若干相同元素 //list<int>::iterator iter = data.begin(); //for(int i=0; i<9; i++){ //++iter; //} //data.insert(iter, 3, 88); //第一个实参是一个指定位置的迭代器 //第二个实参是要插入的元素数目 //第三个实参是要插入的元素 //向列表中插入一个元素序列 //vaector<int> numbers(10, 5); //生成一个序列 //data.insert(--(--data.end()), numbers.begin(), numbers.end()); //第一个实参是指向最后一元素之前的第二个元素迭代器 //2 访问列表中的元素 //通过调用列表front()或back()函数获得对列表中第一个或最后一个元素的引用 //begin()/end(), rbegin()/rend()函数返回双向迭代器 /*#include "stdafx.h" #include <iostream> #include <list> #include <string> using namespace std; int main() { list<string> text; //列表容器 list<string>::iterator iter; //列表容器的迭代器 string sentence; while(getline(cin, sentence, '\n'), !sentence.empty()) text.push_front(sentence); cout<<"循环所有元素:"<<endl; for(iter = text.begin(); iter!=text.end(); iter++){ cout<<*iter<<endl; } //记住:列表不支持对元素的随机访问,因此该迭代器是双向迭代器,襾不是随机访问迭代器 cout<<"循环已经被排序后的列表容器"<<endl; text.sort(); //此输出用list<string>对像的sort()成员来排序内容,因为list<T>容器不提供随机访问迭代器 //所以不能使用在<algorithm>头文件中定义的sort()函数,这就是list<T>模板定义它自己的sort()数成员的原因 for(iter = text.begin(); iter!= text.end(); iter++){ cout<<*iter<<endl; } return 0; }*/ // 3 列表上的其他操作 //clear()函数删除列表中的所有元素,erase()函数允许删除由一个失代器指定的单个元素,或者由一对迭代器以用的方式(序列中的第一个元素和最后一个元素的下一个位置)指定的一个元素序列 //int data[] = {10,22, 4, 56, 89, 77, 13, 9}; //list<int> numbers(data, data+8); //numbers.erase(++numbers.begin()); //numbers.erase(++numbers.begin(), --(--numbers.end())); //remove()函数从列表中删除特定值的元素 //numbers.remove(22); //assign()函数删除列表中的所有元素并将一个对象复制到列表中给定次数,或者复制品由两个迭代器指定一个对像序列 //int data[] = {10,22, 4, 56, 89, 77, 13, 9}; //list<int> numbers(data, data+8); //numbers.assign(10, 99); //后面添加两个99的元素 //numbers.assign(data+1, data+4); //unique()函数将消除列表中相邻的重复元素,因此如果我们先排序内容,应用该函数可以确保所有元素都是碓一的 //int data[] ={10, 22, 4, 10, 89, 22, 89, 10}; //list<int> numbers(data, data+8); //numbers.sort(); //排序 //numbers.unique(); //splice()函数允许我们删除一个列表的全部或一部分,并将它插到中一个列表中 //显然,两个列表必须存储相同类型的元素,下面是使用splice()函数的最简单方式 //int data[] ={1, 2, 3, 4, 5, 6, 7, 8}; //list<int> numbers(data, data+3); //三个元素 1 2 3 //list<int> values(data+4, data+8);//5 6 7 8 //numbers.splice(++numbers.begin(), values);//1 5 6 7 8 2 3 //splice()函数的第一个实参是一个迭代器,指定应在何处插入元素, //第二个实参是插入的元素来自的列表 //这一操作删除values列表中的所有元素并立即将它们插入到numbers列表中的第二个元素前面 //删除源列表中给定位置的元素,并将它们插入到目的地列表给定位置 //int data[] = {1,2,3,4,5,6,7,8}; //list<int> numbers(data, data+3); //1 2 3 //list<int> values(data+4, data+8); //5 6 7 8 //numbres.splice(numbers.begin(), vlaues, --values.end()); //8 1 2 3 //splice()函数的前两个实参与该函数上一个版本相同 //第三个实参是一个迭代器,指定要从源列表中选择的第一个元素的位置,这个位置到末尾的所有元素都被从源列表中删除,并插入目的列表中 //splice()的第三个版本需要四个实参,并从源列表中选择一个元素范围 //int data[] = {1, 2, 3, 4, 5, 6, 7, 8}; //list<int> numbers(data, data+3); //list<int> values(data+4, data+8); //numbres.splice(++numbres.begin(), values, ++values.begin(), --values.end()); //merge()函数删除我们作为一个实参提供列表中的元素,并将它们插入调用该函数的列表中, //然后函数默认会按升序方式排序扩展列表的内容, //int data[] = {1, 2, 3, 4, 5, 6, 7, 8}; //list<int> numbres(data, data+3); //1 2 3 //list<int> values(data+1, data+8);//2 3 4 5 6 7 8 //numbres.merge(values); //这段代码将values的内容合并到numbres中,因此执行此操作后values将变空,接受一个实参的merge()函数默认情况下以升序方式排列结果的顺序 //因为两个列表的值都已经排序 //numbers.sort(greater<int>()); //numbers.sort(greator<int>()); //numbers.merge(values, greater<int>()); //这里我们用在<funcitonal>头文件中定义的,greater<int>()函数对像来指定应以降序方式排序的列表,它们将在相同的序列中合并 //remove_if()函数基于应用一元谓词的结果来删除列表中的元素, /*#include "stdafx.h" #include <functional> #include <iostream> #include <list> using namespace std; template<class T> class is_negative: public std::unary_function<T, bool> { public: result_type operator() (T& value){ return value < 0; } }; //template<class T> class is_negative_ //{ //public: // bool operator() (T& value){ // return value < 0; // } //}; //这里我们不需要<founctional>的#include 指令,因为没有使用基础模板,这样比罗简单 //可能也更容易理解,不过在此包含原始版本只是为了显示如何使用基础模板,如果打算在更通用的上下文中使用谓词 //特别是如果要在函数适配器中使用它,则可能想这么想 template<class T> void listlist(list<T>& data) { for(list<T>::iterator iter = data.begin(); iter != data.end(); iter++){ cout<<*iter<<" "; } cout<<endl; } template<class T> void loadlist(list<T>& data) { T value = 0; while(cin>>value, value != 0){ data.push_back(value); } } int main() { list<int> numbers; cout<<"现在开始输入内容:"<<endl; loadlist(numbers); cout<<"列表内容为:"<<endl; listlist(numbers); numbers.remove_if(is_negative<int>()); cout<<"After applying the remove_if() function the list contains:"<<endl; listlist(numbers); list<double> values; loadlist(values); cout<<"The list contains:"<<endl; listlist(values); values.remove_if(is_negative<double>()); cout<<"After applying the remove_if() function() the list contains."<<endl; listlist(values); list<double> morevalues; loadlist(morevalues); cout<<"显示的moreValues:"<<endl; morevalues.remove_if(is_negative<double>()); cout<<"显示的列表:"<<endl; listlist(morevalues); //开始排序 values.sort(greater<double>()); morevalues.sort(greater<double>()); values.merge(morevalues, greater<double>()); cout<<"合并values和morevalues以后:"<<endl; listlist(values); //不能合在一起去,因为类型不同 //values.sort(greater<double>); //numbers.sort(greater<double>); return 0; } //输出显示了用于int类型和double类型的值的谓词,remove_if()函数依次向列表中的各个元素应用谓词,并删除谓词返回true的元素 */ //10.3.10 使用其他序列容器 //1 队列容器 //queue<T>容器通过适配器实现先进先出存储机制,我们只能向队列的末尾添加或从开头删除元素 //queue<string> names; //queue<string, list<string>> names; //适配器模板的第二个类型形参指定要使用的低层序列容器,队列甜酸器类扮演低层容器类的包装器的角色, //back() 返回对列后端的元素的引用,这个函数有两个版本,一个版本返回const 引用户 // 另一个版本返回非const引用,如果队列为空,则返回的值不确定 //front() 返回对队列前端的元素的引用,这个函数有两个版本,一个版本返回const引用,另一个非const引用 //push() 将实参指定为元素添加的到队列的后端 //pop() 删除队列前端的元素 //size() 返回队列中的元素数目 //empty() 如果队列为空则返回true,否则返回false /*#include "stdafx.h" #include <iostream> #include <string> #include <queue> using namespace std; int main() { queue<string> sayings; string saying; cout<<"添加字符串:"<<endl; while(true){ getline(cin, saying); if(saying.empty()){ break; } sayings.push(saying); } cout<<"现在队列容器的大小为:"<<sayings.size()<<endl; while(!sayings.empty()) { cout<<sayings.front()<<endl; sayings.pop(); } return 0; } */ //2 优先级队列容器 //priority_queue<T>容器是一个队列,它的顶部总是具有最大或最高优先级, //priority_queue<int> numbers; //当向队列中添加元素时确定元素的相对优先级的默认条件是标准less<T>函数对像模板 //我们用push()函数向优先级队列中添加一个元素 //numbres.push(99); //top() 返回对优先级队列前端元素的const引用,它将是容器中最大或最高优先级的元素,如果优先级队列为空,则返回的值不确定 //push() 将实参指定的元素添加到优先级队列中由容器的谓词确定的位置,默认为less<T> //pop() 删除优先级队列前端的元素,它将是容器中最大或优先级最高的元素 //size() 返回优先级队列中的元素数目 //empty() 如果优先级队列为空则返回true,否则返回false //注意: 优先级除列与队列容器的可用函数之间有一个明显的区别,使用优先级队列时不能访问队列后端的元素,只能访问前端的元素 //默认情况下,优先级队列适配类使用的基础容器为vector<T>,我们可以选择指定不同的序列容器作为基础,并选择一个备用函数对像来确定元素的优先级 //priority_queue<int, deque<int>, greater<int>> numbers; //三个模板形参是元素类型 / 要用作基础的容器,要用来排列元素顺序的谓词类型 /*#include "stdafx.h" #include <iostream> #include <vector> #include <functional> #include <string> #include <queue> using namespace std; class Person { public: Person(string first, string second) { firstname = first; secondname = second; } Person(){}; Person(const Person& n){ firstname = n.firstname; secondname = n.secondname; } bool operator<(const Person& p)const{ if(secondname < p.secondname || ((secondname == p.secondname) && (firstname < p.firstname)) ){ return true; } return false; } bool operator> (const Person& n)const { return n < *this; } //重载的>运算可以防止用greater<Person>谓词类型排列Person对像的顺序 void showPerson() const { cout<<firstname<<" "<<secondname<<endl; } private: string firstname; string secondname; }; class PersonComp:binary_function<Person, Person, bool> { public: result_type operator()(const first_argument_type& p1, const second_argument_type& p2) const { return p1 > p2; } private: }; int main() { //定义一个优先级的队列容器 priority_queue<Person, vector<Person>, greater<Person>> people; //不要将模板实例中使用的类型实参greater<Person>与可能作为sort()算法的实参的对像greater<Person>()相混淆 string first, second; while(true){ cout<<"请输入姓:"<<endl; getline(cin,first); if(first.empty()){ break; } cout<<"请输入名:"<<endl; getline(cin,second); people.push(Person(first, second)); } cout<<endl; cout<<"优先队列容器的大小:"<<people.size()<<endl; while(!people.empty()) { people.top().showPerson(); //top()函数返回对队前端对像的一个引用,我们用这个引用调用showPerson()函数来输出姓名 //然后调用pop()删除队列前端的元素,如果不这样做就无法访问下一个元素 //因为优先级队列容器只能读取前端的元素值,不能随机访问 people.pop(); } return 0; }*/ // 3 堆栈容器 //stack<T>容器适配器模板在<stack>头文件中定义,默认情况下基于deque<T>容器实现向下推栈,向下推栈是一种后进先出机制,只能访问最近添加到堆栈的对像 //stack<Person> people; //基础容器可以是支持back() push_back()和pop_back()操作的任何序列容器, //stack<string, list<string>> names; //top() 返回对堆栈顶部元素的引用,如果堆栈为空,则返顺的值不确定,我们可以将返回的引用赋给一个const或非const引用,如果赋予后者,就可以在堆栈中修改对像 //push() 向堆栈顶部添加实参指定的元素 //pop() 删除堆栈的元素 //size() 返回堆栈中的元素数目 //empty() 如果堆栈为空则返回true, 否则返回false /*#include "stdafx.h" #include <iostream> #include <string> #include <stack> #include <list> using namespace std; class Person { public: Person(string first, string second) { firstname = first; secondname = second; } Person(){}; Person(const Person& n){ firstname = n.firstname; secondname = n.secondname; } bool operator<(const Person& p)const{ if(secondname < p.secondname || ((secondname == p.secondname) && (firstname < p.firstname)) ){ return true; } return false; } bool operator> (const Person& n)const { return n < *this; } //重载的>运算可以防止用greater<Person>谓词类型排列Person对像的顺序 void showPerson() const { cout<<firstname<<" "<<secondname<<endl; } private: string firstname; string secondname; }; int main() { stack<Person, list<Person>> people; string first, second; while(true){ cout<<"请输入姓:"<<endl; getline(cin,first); if(first.empty()){ break; } cout<<"请输入名:"<<endl; getline(cin,second); people.push(Person(first, second)); } cout<<endl<<"There are "<<people.size()<<endl; while(!people.empty()){ people.top().showPerson(); people.pop(); } return 0; }*/