C++_标准模板库STL概念介绍1-建立感性认知
标准模板库的英文缩写是STL,即Standard Template Library。
STL里面有什么呢?
它提供了一组表示容器、迭代器、函数对象和算法的模板。
容器是一个与数组类似的单元,可以存储若干值。
STL容器是同质的,即存储的值的类型相同。
算法是完成特定任务的处方(例如对数组进行排序或在链表中查找特定的值)。
迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,属于广义指针。
函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。
STL使得能够构造各种容器(数组、队列、链表)和执行各种操作(搜索、排序和随机排列)。
STL是C++标准的一部分。
STL不是面向对象的编程,而是一种不同的编程模式——泛型编程(generic programming)。
这使得STL在功能和方法上都很有趣。
这篇文章主要通过介绍例子,来对容器、迭代器和算法有感性的认识;领会泛型编程的精神;介绍底层的设计理念,简要地介绍STL。
==================================
一、模板类vector
在计算机中,矢量对应于数组,而不是数学矢量。
在数学中,可以使用N个分量来表示N维数学矢量。
因此从这方面讲,数学矢量类似于一个N维数组。
当然,数学矢量还有一些计算机矢量不具备的特征,例如内乘积和外乘积。
接下来看一下计算矢量有哪些特征:
1、存储了一组可随机访问的值;
2、可以使用索引来直接访问矢量的第n个元素;
3、可以创建一个vector对象,将对象赋给另一个对象,使用[ ]运算符来访问vector元素。
要使类成为通用的,应将它设计为模板类。
STL正是这样做的——在头文件vector中定义了一个vector模板。
要创建vector模板对象,可使用通常的<type>表示法来指出要使用的类型。
另外,vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量:
#include vector
using namespace std;
vector <int> rating(5); //a vector of 5 ints
int n;
cin >> n;
vector<double> scores(n); //a vector of n doubles
由于运算符[ ]被重载,因此创建vector对象后,可以使用通常的数组表示法来访问各个元素:
rating [0] = 9;
for (int i =0; i<n; i++)
cout<< scores[i] <<endl;
分配器:
与string类似,各种STL容器模板都接受一个可选的模板参数,该参数指定使用哪个分配器对象来管理内存。
例如,vector模板的开头与下面类似:
template <class T, class Allocator = allocator<T>>
class vector {...
如果省略该模板参数的值,则容器模板将默认使用allocator<T>类。这个类使用new和delete。
接下来有个示例程序,它使用了这个类。该程序创建了vector对象,一个是int规范、一个是string规范,它们都包含5个元素。
// vect1.cpp -- introducing the vector template
#include <iostream>
#include <string>
#include <vector> const int NUM = ;
int main()
{
using std::vector;
using std::string;
using std::cin;
using std::cout; vector<int> ratings(NUM);
vector<string> titles(NUM);
cout<<"You will do exactly as told.Your will enter\n"<<NUM<<" book titles and your ratings (0-10).\n";
int i;
for (i=; i<NUM;i++)
{
cout << "Enter title #"<<i+<<": ";
getline(cin,titles[i]);
cout<<"Enter your rating (0-10):";
cin >>ratings[i];
cin.get();
}
cout<<"Thank you. You entered the following:\n"<<"Rating\t\Book\n";
for (i=; i<NUM; i++)
{
cout<<ratings[i]<<"\t"<<titles[i]<<endl;
}
return ;
}
==================================
二、可对矢量执行的操作
除了分配空间外,还可以对模板完成一些基本的操作;
STL容器都提供了一些基本方法,其中包括size()——返回容器中元素数目、swap()——交换两个容器的内容、begin()——返回一个指向容器中第一个元素的迭代器、end()——返回一个表示超过容器尾的迭代器。
那么什么是迭代器呢?它实际上是一个广义指针。它可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用和递增。
通过将指针广义化为迭代器,让STL能够为各种不同的容器类提供统一的接口。
该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。
要为vector的double类型规范声明一个迭代器,可以这样做:
vector<double>::iterator pd; //pd an iterator
假设scores是一个vector<double>类型的对象;
vector<double> scores;
则可以使用迭代器pd执行这样的操作:
pd = scores.begin();
*pd = 22.3;
++pd;
可以看到迭代器的行为像一个指针;
另外C++11还有一个自动类型推断的功能如下:
auto pd = scores.begin();
接下来讨论什么是超过结尾(past-to-end)?它是一种迭代器,指向容器最后一个元素后面的一个元素。
这与C风格字符串的最后一个字符空字符类似,空字符是一个值。
而“超过结尾”是一个指向元素的指针(迭代器)。
end()成员函数标识超过结尾的位置。
如果将迭代器设置为容器的第一个元素,并不断地递增,最终它将到达容器的末尾,从而遍历整个容器的内容。
则可以使用下列代码来显示容器的内容:
for(pd = scores.begin(); pd != scores.end(); pd++)
cout<<*pd<< endl;
另外push-back()是一个方便的方法,它将元素添加到容器的末尾。这样做的时候,它将负责管理内存,增加矢量的长度,使之能容纳新的成员。
可以这样写代码:
vector<double> scores;
double temp;
while(cin>>temp && temp>=0)
socres.push_back(temp);
cout<<"Your entered"<<scores.size()<<"scores.\n";
每次循环都给对象添加元素,无需了解元素的数目,只要有内存,就能添加元素。
earse()方法删除容器中指定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。
了解STL如何使用两个迭代器来定义区间非常重要。
第一个迭代器指向区间的起始位置,第二个迭代器指向位于区间终止处的后一个位置。
代码如下,该代码表示删除begin()和begin()+1指向的元素:
scores.erase(scores.begin(), scores.begin()+2);
我们发现STL文档中会使用[ )方法来表示区间。注意,这种不是C++的标准,仅用于文档表达;
[p1, p2)用来表示p1到p2(不包括p2)的区间。这是一种前闭后开的区间。STL容器就是根据这个约定来定义区间范围的。
[begin(), end()]表示集合的所有内容。
insert()方法的功能和erase()相反。它接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个参数定义了要插入的属于另一个对象的新元素区间,该区间通常是另一个容器对象的一部分。
代码如下:
vector<int> old_v;
vector<int> new_v;
...
old_v.insert(old_v.begin(), new_v.begin()+1, new_v.end());
上述代码表示将矢量new_v中的除第一个元素以外的所有元素插入到old_v矢量的第一个元素前面;
另外,对于上面这种情况来说,拥有超尾元素(past-to-end)带来了方便。因为这使得在矢量尾部添加元素非常简单。
old_v.insert(old_v.end(), new_v.begin()+1, new_v.end());
接下来有个示例程序,演示了各个容器方法的使用。
//vect2.cpp -- methods and iterators
#include <iostream>
#include <string>
#include <vector> struct Review {
std::string title;
int rating;
}; bool FillReview(Review & rr);
void ShowReivew(const Review & rr); int main()
{
using std::cout;
using std::vector;
vector<Review> books;
Review temp;
while (FillReview(temp))
book.push_back(temp);
int num = books.size();
if (num >)
{
cout<<"Thank you. Your entered the following:\n"
<<"Rating\tBook\n";
for (int i =; i<num; i++)
ShowReview(books[i]);
cout<<"Reprising:\n"<<"Rating\tBooks\n";
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
vector<Review>oldlist(books);
if (num >)
{
//remove 2 items
books.erase(begin.begin()+, books.begin()+)
cout<< "After erasure:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr); //insert 1 item
books.insert();
cout<<"After insertion:\n";
for (pr =books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
}
books.swap(oldlist);
cout<<"Swapping oldlist with books:\n";
for(pr = books.begin(); pr != books.end();pr++)
ShowReview(*pr);
}
else
cout<<"Nothing entered, nothing gained.\n";
return ;
} bool FillReview(Review & rr)
{
std:;cout<<"Enter book title(quit to quit):";
std::getline(std::cin, rr.title);
if (rr.title == "quit")
return false;
std::cout<<"Enter book rating: ";
std::cin >> rr.rating;
if(!std::cin)
return false;
while(std::cin.get() != '\n')
continue;
return true;
} void ShowReview()
{
std::cout<<rr.rating<<"\t"<<rr.title<<std::endl;
}
==================================
三、对矢量可执行的其他操作
程序员通常要对数组执行很多操作,如搜索、排序、随机排序等;
矢量模板包含了执行这些常见的操作方法吗?没有!
STL从更广泛的角度定义了非成员函数来执行这些操作,既不是为每个容器类定义find()成员函数,而是定义了一个适用于所有容器类的非成员函数find()。
这种设计理念省去了大量的重复工作。
举个例子,假设有8个容器类,需要支持10种操作。如果每个类都有自己的成员函数,则需要定义80个成员函数。
但是采用STL的方法,只需要定义10个非成员函数即可。
在定义新的容器类时,只要遵循正确的指导思想,则它也可以使用已有的10个非成员函数来执行查找、排序等操作。
当然有时候,即使有执行相同任务的非成员函数,STL有时也会定义一个成员函数。这是因为对有些操作来讲使用类的特定算法的效率比通用算法高。
因此,vector的成员函数swap()的效率比非成员函数swap()高,但非成员函数让您能够交换两个类型不同的容器的内容。
接下来讨论3个具有代表性的STL非成员函数:for_each()、random_shuffle()和sort()。
for_each()函数可用于很多函数类,它接受3个参数。前两个是迭代器用于定义容器中的区间,最后一个是指向函数的指针(更普遍地说,最后一个参数是一个函数对象,函数对象将稍后介绍)。
可以将代码:
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
替换为:
for_each(books.begin(),books.end(), ShowReview);
这样可以避免显式地使用迭代器变量。
Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素。
例如,下面的语句随机排列books矢量中的所有元素:
random_shuffle(books.begin(), books.end());
sort()函数也要求容器支持随机访问。该函数有两个版本。
第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的<运算符,以对区间中的元素进行操作。
下面的语句按照升序对coolstuff的内容进行排序,排序时使用内置的<运算符对值进行比较:
vector<int> coolstuff;
...
sort(coolstuff.begin(), coolstuff.end());
如果容器元素是用户定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator<()函数。
例如,如果为Review提供了成员函数或非成员函数operator<(),则可以对包含Review对象的矢量进行排序。
由于Review是一个结构,因此其成员是公有的,这样的非成员函数将为:
bool operator<(const Review & r1, const Review & r2)
{
if(r1.title <r2.title)
return true;
else if (r1.title == r2.title && r1.rating<r2.rating)
return true;
else
return false;
}
有了这样的函数,就可以对包含Review对象的矢量进行排序了;
sort(books.begin(), books.end()); //这两个参数是指定容器区间的迭代器;
上述的排序是按照升序的方式进行,使用内置的<运算符进行排序;
如果想要按照降序的方式的话,可以自己定义一个函数WorseThan();
有了这个函数后,就可以对包含Review对象的book矢量进行降序排序:
sort(books.begin(), books.end(),WorseThan());
接下来这段程序将演示STL非成员函数的用法:
//vect3.cpp -- using STL functions
#include <iostream>
#include <string>
#include <vector>
#include <algorithm> struct Review {
std::string title;
int rating;
} bool operator<(const Review & r1, const Review & r2);
bool worsethan(const Review & r1, const Review & r2);
bool FillReview(Review & rr);
void ShowReview(const Review & rr); int main()
{
using namespace std; vector<Review> books;
Review temp;
while(FillReview(temp))
books.push_back(temp); if(books.size()>)
{
cout<<""<<books.size()<<""<<"";
for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end());
cout<<"";
for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end(),worseThan);
cout<<"";
for_each(books.begin(), books.end(), ShowReview); random_shuffle(books.begin(), books.end());
cout<<"";
for_each(books.begin(), books.end(), ShowReview);
}
else
{
cout<<"No entries";
}
cout<<"Bye.\n";
return ;
} bool operator<(const Review & r1, const Review & r2)
{
if(r1.title < r2.title)
return true;
else(r1.title == r2.title && r1.rating <r2.rating)
return true;
else
return false;
} bool worsethan(const Review & r1, const Review & r2)
{
if(r1.rating < r2.rating)
return true;
else
return false;
} bool FillReview(Review & rr)
{
std::cout<<"Enter book title(quit to quit)";
std::getline(std::cin, rr.title);
if(rr.title == "quit")
return false;
std::cout<<"Enter book rating:";
std::cin>>rr.rating;
if(!std::cin)
return false;
//get rid of rest of input line
while(std::cing.get() != "\n")
continue;
return true;
} void ShowReview(const Review & rr)
{
std::cout<<rr.rating<<"\t"<<rr.title<<std::endl;
}
==================================
四、基于范围的for循环(C++11)
接下来介绍一种新型的for循环方式,叫做基于范围的for循环;
这种for循环其实是为用于STL而设计的;
下面有个示例:
double price[5] = {4.99, 10.99, 6.87, 7.99, 8.49};
for (double x : prices)
cout<<x<<std::endl;
首先在for循环的括号中,声明一个变量,该变量的类型与容器中存储的内容相同。然后指出容器的名称。
接下来,循环体使用指定的变量依次访问容器的每个元素。
由此我们可以改写代码:
将代码:for_each(books.begin(), books.end(), ShowReview);
改写成:for(auto x : books) ShowReview(x);
注意这里修饰x变量的关键字是auto。自动变量,这是因为根据books的类型vector<Review>,编译器可以推断出x的类型为Review,而循环将依次将books中的每个Review对象传递给ShowReview()。
这个是自动类型推断功能。
基于范围的for循环还可以修改容器的内容,诀窍是使用一个引用参数。
void InflateReview(Review &r){r.rating++}
接下来:
for(auto x:books) InflateReview(x);
C++_标准模板库STL概念介绍1-建立感性认知的更多相关文章
- C++_标准模板库STL概念介绍5-其他库与总结
C++还提供了其他一些类库,这些类库更加专用. 例如,头文件complex为复数提供了类模板complex,包含用于float.long和long double的具体化. 这个类提供了标准的复数运算以 ...
- C++_标准模板库STL概念介绍4-算法
STL包含很多处理容器的非成员函数: sort() copy() find() random_shuffle() set_union() set_intersection() set_differen ...
- C++_标准模板库STL概念介绍2-泛型编程
有了之前使用STL的经验后,接下来讨论泛型编程及其底层的理念: 首先我们知道STL只是泛型编程的一种: 而面向对象的编程方式关注的是编程的数据方面: 而泛型编程关注的是算法: 但是,他们之间的一个重要 ...
- C++_标准模板库STL概念介绍3-函数对象
函数对象也叫做函数符(functor). 函数符是可以以函数方式和( )结合使用的任意对象. 包括函数名,指向函数的指针,重载了()运算符的类对象. 可以这样定义一个类: class Linear { ...
- STL学习系列之一——标准模板库STL介绍
库是一系列程序组件的集合,他们可以在不同的程序中重复使用.C++语言按照传统的习惯,提供了由各种各样的函数组成的库,用于完成诸如输入/输出.数学计算等功能. 1. STL介绍 标准模板库STL是当今每 ...
- C++ 标准模板库STL 队列 queue 使用方法与应用介绍
C++ 标准模板库STL 队列 queue 使用方法与应用介绍 queue queue模板类的定义在<queue>头文件中. 与stack模板类很相似,queue模板类也需要两个模板参数, ...
- 标准模板库--STL
标准模板库STL 1.泛型程序设计 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1.面向对象的思想:继承和多态,标准类库 2.泛型程序设计(generic progra ...
- 实验8 标准模板库STL
一.实验目的与要求: 了解标准模板库STL中的容器.迭代器.函数对象和算法等基本概念. 掌握STL,并能应用STL解决实际问题. 二.实验过程: 完成实验8标准模板库STL中练习题,见:http:// ...
- cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器
cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器map(映射,key不能重复,一对一对的,value_type(1, "one")),mu ...
随机推荐
- 第01章 开发准备(对最新版的RN进行了升级)1-2+项目技术分解
- 【bzoj1016】[JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 4863 Solved: 1973[Submit][St ...
- Tensorflow学习(练习)—CPU训练模型
Mask R-CNN - Train on Shapes Dataset This notebook shows how to train Mask R-CNN on your own dataset ...
- 434. Number of Segments in a String 字符串中的单词个数
[抄题]: Count the number of segments in a string, where a segment is defined to be a contiguous sequen ...
- 获取表中唯一字符串uuid,可用于随机文件名
在mysql数据库中,可以使用uuid()语句来生成一个UUID:例如:mysql> select uuid();+--------------------------------------+ ...
- VirtualBox安装增强功能(Linux)
我们在安装之前,必须得先安装好它所需要的依赖包,不然安装过程必定会出现错误! 一.安装依赖包 #yum install kernel-headers #yum install kernel-devel ...
- Java Java7处理异常新特性
- Python htmlTestRunner生成测试报告Demo
#该代码段是ReadTxt_demo.py 的代码,用户读取txt 文件中的用户信息. #ReadTxt_demo.py def readTxt(filePath): fo = open(filePa ...
- FreeMarker的实例通俗理解
1.把包lib/freemarker.jar拷贝到项目中 2. 在WEB-INF下新建文件夹templates 在templates下新建test.ftl文件 内容为: <html> ...
- How do I create a .pyc file?
Python automatically compiles your script to compiled code, so called byte code, before running it. ...