C++标准模板库 (STL)主要由两种组件构成:
vector
, list
,set
, map
等类find()
,sort()
,replacet()
,merge()
等等。容器(container):
关于STL,可以主要查阅下文进行详细了解:
https://blog.csdn.net/qq_50285142/article/details/114026148
/*********************************************************************
程序名:ch3.1
版权:
作者:
日期: 2023-08-10 10:19
说明:给定一个存储整数的 vector,以及一个整数值.
如果此值存在于vector 内,我们必须返回一个指针指向该值;
反之则返回O,表示此值并不在vector内。
*********************************************************************/
#include
#include
using namespace std;
int *find_elem(vector &vec, int value);
void display_elems(vector vec, ostream &os = cout);
int main() {
vector vec{1, 2, 3, 4, 5};
int *p = find_elem(vec, 3);
cout << *p << endl;
*p = 8;
display_elems(vec);
return 0;
}
int *find_elem(vector &vec, int value) {
for (int i = 0; i < vec.size(); ++i) {
if (vec[i] == value) {
return &vec[i];
}
}
return 0;
};
void display_elems(vector vec, ostream &os) {
os << "\nPentagonal Numeric Series\n\t";
for (int ix = 0; ix < vec.size(); ++ix)
os << vec[ix] << ' ';
os << endl;
};
/*********************************************************************
程序名:ch3.1.2
版权:
作者:
日期: 2023-08-10 11:15
说明:基于ch3.1,想办法让这个函数不仅可以处理整数,
也可以处理任何型别——前提是该型别定义有 equality (相等)运算符。
*********************************************************************/
#include
#include
using namespace std;
template
T1 *find_elem(vector &vec, T2 value) {
for (int i = 0; i < vec.size(); ++i) {
if (vec[i] == value) {
return &vec[i];
}
}
return 0;
};
template
void display_elems(vector vec, ostream &os = cout) {
os << "\nPentagonal Numeric Series\n\t";
for (int ix = 0; ix < vec.size(); ++ix)
os << vec[ix] << ' ';
os << "\n\n" << endl;
};
int main() {
vector ivec{1, 2, 3, 4, 5};
cout << "int :" << endl;
int *p1 = find_elem(ivec, 3);
cout << *p1 << endl;
*p1 = 8;
display_elems(ivec);
cout << "float :" << endl;
vector fvec{ 2.5, 24.8, 18.7, 4.1, 23.9 };
float *p2 = find_elem(fvec, 2.5);
cout << *p2 << endl;
*p2 = 3.1;
display_elems(fvec);
cout << "string :" << endl;
vector svec{ "we", "were", "her", "pride", "of", "ten" };
string *p3 = find_elem(svec, "her");
cout << *p3 << endl;
*p3 = "majin";
display_elems(svec);
return 0;
}
在定义**find_elem()**时,如果只定义一个template会出现报错:
template
T1 *find_elem(vector &vec, T1 value) {
// ...
}
以上都是将vector整体当参数传入find_elem(),如果数据结构是array,并且不考虑函数重载的实现方式,该怎么做?
/*********************************************************************
程序名:ch3.1.3
版权:
作者:
日期: 2023-08-10 11:56
说明:array下标的相关操作
*********************************************************************/
#include
using namespace std;
template
T1 *find_elem(T1 *array, int size, T1 &value) {
if (!array || size < 1) {
return 0;
}
for (int i = 0; i < size; ++i) {
if (array[i] == value) {
return &array[i]; // 返回的是指针,所以需要&
}
}
return 0;
}
int main() {
int iarray[] = { 12, 70, 2, 169, 1, 5, 29 };
int value = 12;
int *p = find_elem(iarray, 6, value);
cout << *p << endl;
return 0;
}
因为事实上所谓下标操作就是将array 的起始地址加上索引值,产生出某个元素的地址、然后该地址再被提领(dereference)以返回元素值.
[ ] : subscript(下标):下标操作就是将array 的起始地址加上索引值,产生出某个元素的地址。然后该地址再被提领 ***** 以返回元素值.例如:
array = &array = array 的起始地址
//返回array的第3个元素:
array[2] = *(array+2)
如何将array的声明从参数表中完全移除?
/*********************************************************************
程序名: ch3.1.4
版权:
作者:
日期: 2023-08-10 14:53
说明:我们使用第二个指针来取代参数size。
此指针扮演哨兵的角色。
这个版本可以让我们将 array的声明从参数表中完全移除:
*********************************************************************/
#include
using namespace std;
template
T1 *find_elem(T1 *first, T1 *last, T1 &value) {
if (!first || !last) {
return 0;
}
//当first不等于last时,就把value拿来和 first所指的元素比较
//如果两者相等,便返回first,否则将first累加1,令它指向下一个元素
for (; first != last; ++first) { //这里是用地址进行比较、自增
if (*first == value) {
return first; // 返回的是指针,所以需要&
}
}
return 0;
}
int main() {
int ia[] = { 12, 70, 2, 169, 1, 5, 29 };
int *pi = find_elem(ia, ia + 8, ia[3]);
cout << *pi << endl;
string sa[] = { "we", "were", "her", "pride", "of", "ten" };
string *ps = find_elem(sa, sa + 6, sa[3]);
cout << *ps << endl;
return 0;
}
取vector的第一个元素、最后一个元素的两种实现方法:
cout << "int :" << ivec[0] << endl;
cout << "int :" << &ivec.front() << endl;// .front返回 v 中第一个元素的引用
cout << "int :" << ivec[ivec.size()-1] << endl;
cout << "int :" << &ivec.back() << endl; // .back() :返回 v 中最后一个元素的引用
具体实现方法:
/*********************************************************************
程序名: ch3.1.5
版权:
作者:
日期: 2023-08-10 15:10
说明: 将vector的元素传人**find_elem()**,而不指明该vector。
*********************************************************************/
#include
#include
using namespace std;
template
// 取用第一个元素的地址
inline T1 *begin(vector &vec) {
return vec.empty() ? 0 : &vec[0];
}
template
// 取用最后一个元素的地址
inline T2 *end(vector &vec) {
return vec.empty() ? 0 : &vec[vec.size()] - 1;
}
template
T3 *find_elem(T3 *first, T3 *last, T3 &value) {
if (!first || !last) {
return 0;
}
//当first不等于last时,就把value拿来和 first所指的元素比较
//如果两者相等,便返回first,否则将first累加1,令它指向下一个元素
for (; first != last; ++first) { //这里是用地址进行比较、自增
if (*first == value) {
return first; // 返回的是指针,所以需要&
}
}
return 0;
}
int main() {
vector ivec{ 12, 70, 2, 169, 1, 5, 29};
int *pi = find_elem(begin(ivec), end(ivec) + 8, ivec[6]);
cout << *pi << endl;
return 0;
}
list 也是一个容器,不同的是,list 的元素以一组指针相互链接 (linked):前向 (forward)指针用来寻址下一个(next)元素,回向 (backward)指针用来寻址上一个(preceding)元素
因此,指针的算术运算并不适用于 list,因为指针的算术运算必须首先假设所有元都存储在连续空间里,然后才能根据当前的指针,加上元素大小之后,指向下一个元素
解决这个问题的办法是,在底层指针的行为之上提供一层抽象化机制,取代程序原本的**“指针直接操作”**方式,我们把底层指针的处理通通置于此抽象层中,让用户不需直接面对指针的操作,这个技巧使得我们只需提供一份 find()函数,便可以处理标准程序所提供的所有容器类,3.2节将会详细说明
首次,在说泛型指针之前,先说说特定指针,特定指针,顾名思义,就是有明确的类型的指针,如:int *
,char *
等等。
那泛型指针,则为没有数据类型的地址,即 void *。许多库函数中也有此类型的泛型指针,如:malloc( ),memset( )等等。
***为什么学习泛型指针,原因就是*泛型指针可以让我们免去定义一个针对vector的指针、定义一个针对list的指针、针对array的指针,省工作量。
- 搜索(search):
find(), count(), adjacent_find()
(搜寻第一组相邻且值重复的元素)find_if(), count_if(), binary_search(), find_first_of()
(搜索某些元素首次出现的位置)- 排序(sort)和次序整理(ordering):
merge(), partial_sort(), partition()
(切割)random_shuffle(), reverse(), rotate(), sort()
- 复制(copy)、删除(deletion)、替换(substitution):
copy(), remove(), remove_if(), replace(), replace_if(), swap(), unique()
(去除重复)- 关系(relational):
equal(), includes(), mismatch()
(比较两个序列是否匹配)- 生成(generation)与质变(mutation):
fill(), for_each(), generate(), transform()
- 数值(numeric):
accmulate(), adjacent_difference()
(相邻差)partial_sum(), inner_product()
(内积)- 集合(set):
set_union()
(并集),set_difference()
(差集)
在C ++中,作用域运算符为::。它用于以下目的。
#include
using namespace std;
int x = 5;
int main() {
int x = 10;
cout << "Value of global x is " << ::x;
cout << "\nValue of local x is " << x;
return 0;
}
输出:
全局x的值为0
本地x的值为10
#include
using namespace std;
class A {
public:
// 仅声明
void fun();
};
//类外的定义使用
void A::fun() {
cout << "outside fun() called";
}
int main() {
A a;
a.fun();
return 0;
}
/*********************************************************************
程序名:
版权:
作者:
日期: 2023-07-21 15:53
说明:
*********************************************************************/
#include
using namespace std;
class Test {
static int x;
public:
static int y;
void func(int x) {
// 我们可以通关::访问类的静态变量
// 即使存在一个局部变量
cout << "Value of static x is " << Test::x;
cout << "\nValue of local x is " << x;
}
};
//在c++中,静态成员必须显式定义
int Test::x = 1;
int Test::y = 2;
int main() {
Test obj;
int x = 3 ;
obj.func(x);
cout << "\nTest::y = " << Test::y;
return 0;
}
如果两个祖先类中存在相同的变量名,则可以使用作用域运算符进行区分。
#include
using namespace std;
class A {
protected:
int x;
public:
A() {
x = 10;
};
};
class B {
protected:
int x;
public:
B() {
x = 20;
};
};
class C: public A, public B { // 继承?
public:
void fun() {
cout << "A's x is " << A::x;
cout << "\nB's x is " << B::x;
}
};
int main() {
C c;
c.fun();
return 0;
}
如果两个命名空间中都存在一个具有相同名称的类,则可以将名称空间名称与作用域解析运算符一起使用,以引用该类而不会发生任何冲突
#include
int main(){
std::cout << "Hello" << std::endl;
}
如果另一个类中存在一个类,我们可以使用嵌套类使用作用域运算符来引用嵌套的类
#include
using namespace std;
class outside {
public:
int x;
class inside {
public:
int x;
static int y;
int foo();
};
};
// 类静态成员变量需要显示定义
int outside::inside::y = 5;
int main() {
outside A;
outside::inside B;
A.x = 10;
cout << A.x << endl;
cout << B.y << endl;
}
/*********************************************************************
说明:用翻修过的find_elem ()来处理array.vector 和list
*********************************************************************/
#include
#include
#include //这个头文件得写,因为你用到了list,书上没说。。。。
using namespace std;
template
void display(const vector &vec, ostream &os = cout) {
typename vector::const_iterator iter = vec.begin(); //原书中未在vector前加typename,你不加,编译器给你报错。(去掉试试)
typename vector::const_iterator end_it = vec.end(); //如果是面对const vector,则用const_iterator来进行操作
//如果面对vector,就用iterator即可
//若vec是空vector,则iter=end_it,for循环不会被执行
for (; iter != end_it; ++iter) {
os << *iter << ' ';
}
os << endl;
}
//重新实现find()函数
templateIteratorType find(IteratorType first, IteratorType last,const elemType &value) {
for (; first != last; ++first) {
if (value == *first) {
return first;
}
}
return last;//走到这里证明没找到和value一样的元素,直接返回last指针。。。
}
int main() {
const int asize = 8;
int ia[asize] = {1, 1, 2, 3, 5, 8, 13, 21};
int *pia = find_elem(ia, ia + asize, 3);
if (pia != ia + asize) {//这里对应着return last,在传参的时候last=ia+asize
cout << "*pia:" << *pia << endl;//走进了if,证明在容器里找到了指定元素
}
vector ivec(ia, ia + asize);
vector::iterator it; //定义泛型指针
it = find_elem(ivec.begin(), ivec.end(), 3);
if (it != ivec.end()) {
cout << *it << endl;
}
list ilist(ia, ia + asize);
list::iterator iter; //定义泛型指针
iter = find_elem(ilist.begin(), ilist.end(), 3);
if (iter != ilist.end()) {
cout << *iter << endl;
}
return 0;
}
仔细看看这段代码:
void display(const vector &vec, ostream &os = cout) {
typename vector::const_iterator iter = vec.begin(); //原书中未在vector前加typename,你不加,编译器给你报错。(去掉试试)
typename vector::const_iterator end_it = vec.end(); //如果是面对const vector,则用const_iterator来进行操作
//如果面对vector,就用iterator即可
//若vec是空vector,则iter=end_it,for循环不会被执行
for (; iter != end_it; ++iter) {
os << *iter << ' ';
}
os << endl;
}
iter被定义为一个iterator,iter指向一个vector,vector元素类型为elemType(任意类型)。iter的初值指向vector对象的首元素。双冒号(::)表示此iterator(泛型指针)是位于elemType类型的vector
定义内的嵌套类型
采用一般指针的提领方式,即iter
即课提领iter
指针来取得iter
指针指向的对象的元素值。
如果想用泛型指针调用底部的string
元素(类型的对象)所提供的操作(比如.size(),.length()等),
用iter->
箭头运算符.
#include
#include
using namespace std;
int main() {
string str[] = {"hello", "world", "this", "find", "gank", "pink", "that", "when", "how", "cpp"};
vector cs_vec(str, str + 10);
vector::const_iterator iter = cs_vec.begin(); //泛型指针定义并初始化为指向string类型vector对象的首元素
cout << "string value of elemnt:" << *iter; //泛型指针提领操作获得其指向的对象的值
cout << "(" << iter->size() << "):" << *iter
<< endl; //相当于cs_vec[0].size()(打个比方),并且cs_vec[0]是个string类型的字符串。
}
equality
(==)和 nequality
(!=) 运算符,返回true
或 false
.
assignment ()
运算符,将某个容器复制给另一个容器
empty()
会在容器无任何元素时返 true
,否则返回 false
size()
容器当前含有元素的数目
clear()
删除所有元素
begin()
返回一个 iterator
,指向容器的第一个元素
end()
返回一个 iterator
,指向容器的最后一个元亲的下一个位置。
通常我们在容器身上进行的送代操作都是始于 begin()
而终于 end()
所有容器都提供insert()
用以安插元素,以及提供 erase()
用以删除元素
insert()
将单一或某个范围内的安插到容器内- 将容器内的单一元案或某个范围内的元素删除
erase()
void comp(vector &v1, vector &v2) {
if (v1 == v2) {
return;
}
if (v1.empty() || v2.empty() ) {
return;
}
vector temp;
//将较大的 vector 值给 temp
temp = v1.size() > v2.size() ? v1 : v2;
}
参考文档
特征 | 优点 | 缺点 | 场景 | 头文件 | |
---|---|---|---|---|---|
vector | 内存空间是连续的,可拓展的数组(动态数组) | 全端查询效率高 | 中部的插入、删除效率低 | 数列型,对象简单,变化较小,并且频繁随机访问 | #include |
deque | 一段一段的定量连续空间构成,按页或块来分配存储器的,每页包含固定数目的元素 | 查询效率高,两端操作 | 不适合中间插入删除操作;占用内存多 | 适用于既要频繁随机存取,又要关心两端数据的插入与删除的场景。 | #include |
list | 双向链表,每个元素都是放在一块内存中,他的内存空间可以是不连续的,通过指针来进行数据的访问。访问开始和最后两个元素最快,其他元素的访问时间一样 | 内存不连续,动态操作,可在任意位置插入或删除且效率高。 | 不支持随机访问[] 。查询效率低 |
适用于经常进行插入和删除操作并且不经常随机访问的场景。 | #include |
set | 红黑树实现,其内部元素依据有序和不重复原则 | 删除效率高,不需要做内存拷贝和内存移动。 | 每次插入值的时候,都需要调整红黑树,效率有一定影响。 | 适用于经常查找一个元素是否在某群集中且需要排序的场景。 | #include |
map | 红黑树实现,元素都是 Key - value 所形成的一个对组,每个元素有一个键,每一个键只能出现一次 |
元素查找,且能把一个值映射成另一个值,可以创建字典。 | 每次插入值的时候,都需要调整红黑树,效率有一定影响。 | 适用于需要存储一个数据字典,并要求方便地根据key找value的场景。 | #include |
使用那个容器?:
1、如果需要高效的随机存取,不在乎插入和删除的效率,使用 vector。
2、如果需要大量的插入和删除元素,不关心随机存取的效率,使用 list。
3、如果需要随机存取,并且关心两端数据的插入和删除效率,使用 deque。
4、如果打算存储数据字典,并且要求方便地根据 key 找到 value,一对一的情况使用 map,一对多的情况使用 multimap。
5、如果打算查找一个元素是否存在于某集合中,唯一存在的情况使用 set,不唯一存在的情况使用 multiset。
各容器的时间复杂度分析
vector
在头部和中间位置插入和删除的时间复杂度为 O(N)
,在尾部插入和删除的时间复杂度为 O(1)
,查找的时间复杂度为 O(1)
;deque
在中间位置插入和删除的时间复杂度为 O(N)
,在头部和尾部插入和删除的时间复杂度为 O(1)
,查找的时间复杂度为 O(1)
;list
在任意位置插入和删除的时间复杂度都为 O(1)
,查找的时间复杂度为 O(N)
;set
和 map
都是通过红黑树实现,因此插入、删除和查找操作的时间复杂度都是 O(log N)
。#include
#include
#include
#include
#include
#include
list slist; //产生空容器
list ilist(32); // 产生特定大小的容器
vector ivec(10, -1);//产生特定大小的容器并为每个元素指定初值(10个-1)
int ia[3] = {
1, 2, 3
};
vector ivec(ia, ia + 3); // 通过array产生容器
list slist;
list slist2(slsit);// 复制容器产生心容器
使用泛型算法必须包含头文件#include
以vector
为例子,构建一个函数is_elem
,若给定的值在vector
中,返回true
,反之返回false
有以下4中可能被采用的泛型算法:
find()
用于搜索无序集合中是否存在某值,搜索范围由iterator[first,last)
标出。若找到就返回一个iterator指向该值,否则返回一个iterator指向last
,属于linear search。binary_search()
用于搜索有序集合,属于binary search,效率比find()
高。count()
返回数值相符的元素数目。search()
比对某个容器内是否存在某个子序列,存在则返回一个iterator指向子序列起始处,否则指向容器末尾。如给定数序[1,3,5,7,2,9]
,需要搜寻[5,7,2]
,则返回一个iter
,指向下标[2]
由于我们的 Fibonacci vector
必定以增顺序储其值,因此,binary_search()
是我们的佳选择
max_element()
可以返回一对iterator所表示的数列范围内最大的元素值。开始我写了一个函数,它可以找出 vector 内小于 10 的所有元素。然而函数过于死板,没有弹性。
vector less_than_10(const vector &vec) {
vector nvec;
for (int ix = 0; ix < vec.size(); ++ix) {
if (vec[ix] < 10) {
nvec.push_back(vec[ix]);
}
}
return nvec;
}
接下来我为函数加上一个数值参数,让用户得以指定某个数值,以此和 vector 中的元素做比较
vector less_than(const vector &vec,int less_than_val)
后来,我又加上一个新参数:一个函数指针,让用户得以指定比较方式(大于小于或者等于)。
实现方法:加第三个参数 pred,用它来指定一个函数指针,其参数表有两个整数,返回值为 bool.
#include
#include
using namespace std;
void display ( vector vec, ostream &os = cout) {
for (int i = 0; i < vec.size(); ++i) {
os << vec[i] << ' ';
}
cout << endl;
}
bool less_than(int v1, int v2) { // <
return v1 < v2 ? true : false;
}
bool greater_than(int v1, int v2) { // >
return v1 > v2 ? true : false;
}
vector filter(const vector &vec, int filter_value, bool (*pred)(int, int)) {
vector nvec;
for (int ix = 0; ix < vec.size(); ++ix) {
// 调用 pred 所指函数
// 比较 vec[ix] filter_value
if (pred(vec[ix], filter_value)) {
nvec.push_back((vec[ix]));
}
}
return nvec;
}
int main() {
int ia[8] = {
11, 2, 3, 34, 5, 99, 10, 8,
};
vector ivec(ia, ia + 8); // 通过array产生容器
vector rvec = filter(ivec, 10, less_than);
display(rvec);
return 0;
}
find(first,last,val)
: [first, last]
用于指定该函数的查找范围;val
为要查找的目标元素。
当 find()
函数查找成功时,其指向的是在 [first, last]
区域内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last
相同。
#include
#include
#include
// 找vector含有相同数的数量
int count_occurs(const vector &vec, int val) {
vector::const_iterator iter = vec.begin(); // 定义一个泛型指针iter,指向vector的第一个元素
int occurs_count = 0;
while ((iter = find(iter, vec.end(), val)) != vec.end()) {
// 找到了
++ occurs_count;
++iter;
};
return occurs_count;
}
int main() {
int ia[8] = {
11, 2, 3, 34, 5, 10, 10, 8,
};
vector ivec(ia, ia + 8); // 通过array产生容器
int count = count_occurs(ivec, 10);
cout << count << endl;
return 0;
}
function object
的概念,得以将某组行为传给函数,比函数指针效率更高必须含入头文件:#include
然后,我引人function object
的概念,使我们得以将某组行为传给函数,此法比函数指针的做法效率更高。我也带领各位简短的了解标准程序库提供的 function obiects
。
所谓function objects
,是某种 class
的实体对象,这类 class
对 function call
运算符进行了重载操作,如此一来,可使 function obiect
被当成一般函数来使用
6个算术运算
plus
= return x + y minus
= return x - y negate
= return - x; multiplies
= return x * y divides
= return x / y; modulus
= return x % y
6个关系
less
= return x < y less_equal
= return x <= y greater
= return x > y greater_equal
= return x > = y equal_to
= return x == y not_equal_to
= return x! = y
3个逻辑运算
logical_and
= return x && y logical_or
= return x || y logical_not
= return ! x
举个例子: 对vector进行降序
#include
#include
#include // 使用greater必须含入
#include // 使用sort必须含入
using namespace std;
void display ( vector vec, ostream &os = cout) {
for (int i = 0; i < vec.size(); ++i) {
os << vec[i] << ' ';
}
cout << endl;
}
int main() {
int ia[8] = {
11, 2, 3, 34, 5, 10, 10, 8,
};
vector ivec(ia, ia + 8); // 通过array产生容器
sort(ivec.begin(), ivec.end(), greater());
display(ivec);
}
function object adapter会对function object进行修改操作。绑定适配器(binder adapter)会将function object的参数绑定到某个特定值,使二元函数对象转化为一元。bind1st
将指定值绑定到第一操作数,bind2nd
则绑定在第二操作数上。
#include
#include
#include
using namespace std;
void display ( vector vec, ostream &os = cout) {
for (int i = 0; i < vec.size(); ++i) {
os << vec[i] << ' ';
}
cout << endl;
}
// 使用适配器bind2nd()
// find_if() : 前两个参数与find()一致,最后一个参数是传入能返回true or false 的适配器
vector filter_bind(const vector &vec, int val) {
vector nvec;
vector::const_iterator iter = vec.begin();
// bind2nd( less, val 1;
//会把 va1绑定于 lesg的第二个参数身上
//于是,less 会将每个元素拿来和 val比较
while ((iter = find_if(iter, vec.end(), bind2nd(less(), val))) != vec.end()) {
nvec.push_back(*iter);
iter++;
}
return nvec;
}
int main() {
int ia[8] = {
11, 2, 3, 34, 5, 10, 10, 8,
};
vector ivec(ia, ia + 8); // 通过array产生容器
vector rvec = filter_bind(ivec, 9);
display(rvec);
return 0;
}
C++ STL bind1st bind2nd bind 的使用
function template
的方式重新实现函数。最后,我将函数以 function template
的方式重新实现。为了支持多种容器,我传人一对 iterators
,标示出一组元素范围:为了支持多种元素型别,我将元素型别参数化,也将施用于元素身上的“比较操作”参数化,以便得以同时支持函数指针和 function obiect 两种方式。现在,我们的函数和元素型别无关,也和比较操作无关,更和容器型别无关。简单地说,我们已经将最初的函数转化为一个泛型算法了
/*********************************************************************
说明:目的:找出小于/大于某指定值的元素的值。
*********************************************************************/
#include
#include
#include
#include
using namespace std;
//找出比某特定元素大或者小的元素扔进新的容器的函数泛型化,此(模板)函数支持不同的容器,不同的元素类型,支持函数指针和function object两种方式(比较操作参数化)
//在容器的[first,last)范围(first,last均为泛型指针)中,找出比某特定元素大或者小的元素扔进新的容器,扔进新容器的什么位置由at这个泛型指针决定。
//特定元素是val,类型自拟,然后你可以传入function object或者函数指针来决定比较操作,是大于特定元素的扔进新容器还是小于特定元素的扔进新容器
template
T1 filter(T1 first, T1 last, T2 at, const T3 &ElemType, comp pred) {
//find_if为泛型算法,需要algorithm头文件支持,它会返回一个泛型指针给first
//传入的参数一和二是标定查找范围,参数三是一个函数指针或者function object也行。bind2nd也需要functional头文件支持,目的是把第二个参数绑定(固定)
while ((first = find_if(first, last, bind2nd(pred, ElemType))) != last) {
cout << "found value:" << *first << endl;
//找到了,肯定要复制给新容器,就让at指向接收复制的元素的容器的某个位置(一般是首个元素的位置),
//然后解除引用让找到的元素赋值给at指向的元素(或者说把找到的元素复制到at指向的位置上),
//然后,复制成功后at往下挪一个,以便接收后续复制过来的元素,first也往后挪一个,
//是要缩小范围去寻找是否还有满足规则的元素,并扔给at指向的位置,也是防止某个元素被访问2次以减慢效率
*at++ = *first++; // 先赋值后自加
}
return at;//最后返回at指向的位置的地址即可
}
int main() {
const int elem_size = 8;
int ia[elem_size] = {12, 8, 43, 0, 6, 21, 3, 7};
int ia2[elem_size];
cout << "filtering integer array for values less than 8\n";
filter(ia, ia + elem_size, ia2, elem_size,
less()); //对应上面的filter函数定义来看,这里at就指向了存储过滤好的元素的array的首地址(首个位置)
//注意function object的类型是否和vector、array所匹配。function object的()不要少!!!!!
vectorivec(ia, ia + elem_size);
vectorivec2(elem_size);
cout << "filtering integer vector for values greater than 8\n";
filter(ivec.begin(), ivec.end(), ivec2.begin(), elem_size,
greater()); //这里at就指向了存储过滤好的元素的vector的首地址(首个位置),注意function object的类型
//是否和vector、array所匹配。vector、list等容器,不要和array混为一谈,划定查找范围时,.begin(),.end()等不要写成vector的名字及名字+容器容量!!!!
return 0;
}
map(#include
被定义为一对(pair)
数值,key通常是一个字符串,扮演索引角色,另一个数值是value。
map对象有一个名为first
的member,对应于key,另有一个名为second
的member对应于value。
map
,给初值,动态输入 //对文内每个字的出现次数加以分析的程序
//1 直接赋值
map words;
words["vermeer"] = 1;
words["majin"] = 2;
//2 动态输入
string tword;
while (cin >> tword)
words[tword]++;
words["majin"]
,这种方式有一个缺点:如果我们用来索引的那个 key 并不存在于 map 内,则那个 key 会自动被加入 map 中,而其相应的 value 会被设为所属型别默认值。
words.find("majin")
,如果 key
已置于其中,find()
会返回一个 iterator
,指向 key/vaue
形成的一个 pair
。反之则返 end()
//对文内每个字的出现次数加以分析的程序
#include
#include
map.count()
会返某特定项在 map 内的个数
//对文内每个字的出现次数加以分析的程序
#include
#include
set(#include
由一群key组合而成。如果想知道某值是否存在于某个集合内,可以用set。任何一个key值在set内也最多只会有一份。
less_than
运算符进行排列。set
,给初值 set word_ex; // 空set
int ia[10] = {1,3,5,8,5,3,1,5,8,1};
vector vec(ia, ia+10);
set iset(vec.begin(), vec.end());//{1,3,5,8}
set
的常用方法int ia[10]={1,3,5,8,5,3,1,5,8,1};
vectorvec(ia,ia+10);
setiset(vec.begin(),vec.end());
iset.insert(ival);//给set加入单一元素,用单参数的insert():
iset.insert(vec.begin(),vec.end());//给set加入多个元素,用双参数的insert():
set
的迭代set::iterator it=iset.begin();
for(;it!=iset.end();++it)
{
cout<<*it<<' ';
}
cout<
(当函数是将元素赋值到某个已存在的容器上时,程序员要考虑目的端容器不够大的问题,所以)标准库提供了insertion adapter以避免使用容器的赋值运算符#include
。
3.6中的filter()
、泛型算法如copy()
,每个算法都接受一个 iterator
标示出复制的起始位置每当复制一个元索时,其值会被赋值,iterator
则会递增至下个位置,我们必须保证在每一次复制操作中,目的端容器都具有够大的容量以存储这些被值进来的元素。
而Iterator Inserter
就是为了不用再规定固定大小的目标(接收复制元素的)容器
back_inserter(),以容器的push_back()
来执行插入(复制)操作,适合vector,传入b_i的参数为(目标)容器本身。
int ia[8] = {12, 8, 43, 0, 6, 21, 3, 7};
vector ivec(ia, ia + 8);
vectorresult_vec;
unique_copy(ivec.begin(), ivec.end(), back_inserter(result_vec));// 把ivec这个容器的元素从首到尾依次插进result_vec这个容器里
display(result_vec);
return 0;
inserter(),以容器的insert()
函数执行插入(复制)操作,inserter函数接收两个参数,一个(目标)容器,一个指向(目标)容器内的插入操作的起点的泛型指针
int ia[8] = {12, 8, 43, 0, 6, 21, 3, 7};
vector svec(ia, ia + 8);
vectorsvec_res;
unique_copy(svec.begin(), svec.end(), inserter(svec_res, svec_res.begin()));
return 0;
front_inserter(),以容器的push_front()
函数执行插入(复制)操作。适用于list和deque
listilist_clone;
copy(ilist.begin(),ilist.end(),front_inserter(ilist_clone));
//front_inserter()只接受一个参数,即目标容器
如3.6节,ivec2
定义时不直接设定大小,在赋值操作*at++ = *first++
时会报错,直接设定大小,又容易产生内存泄漏。得到的ivec2 = {12,43,21,0,0,0,0,0}
但是一且将 ivec2
传给insertion adapte
,元素的赋值操作 即被替换为安插操作,如果只在 vector 的末端安插元素,效率会比较高,因此我们选用 back inserter.
#include
...
filter(ivec.begin(), ivec.end(), back_inserter(ivec2), elem_size,
greater())
标准库定义了供输入输出使用的iostream iterator
类,称为istream_iterator
和ostream_iterator
,分别支持单一类型的元素读取和写入(#include
)。
如果你想从标准输入设备读取数据,并可能存在某个地方(比如容器里),然后操作一番数据(或容器),然后再输出(写回)到标准输出设备中,那么你需要iostream Iterator
输入是istream_iterator
,输出是ostream_iterator
// 输入:
//first
istream_iterator is(cin);//绑至标准输入设备
//last标示要读取的最后一个元素的下一个位置
istream_iterator eof;//只要定义istream_iterator时不为其指定istream对象,它便代表了end-of-file
// 输出:
//绑至标准输出设备
//第二个参数用来表示各个元素被输出时的分隔符,
ostream_iterator os(cout," ");
//用法举例: copy将存储在text中的每个元素一一写入os所标示的ostream上,每个元素之间一个空格。
copy(is,eof,back_inserter(text));//采用insertion adapter里的back_inserter()函数更有效率
copy(text.begin(),text.end(),os);
...
#include
int main()
{
ifstream in_file("input_file.txt");
ofstream out_file("output_file.txt");
if(!in_file || !out_file)
{
cerr << "!!unable to open the necessary files.\n";
return -1;
}
//将 istream_iterator 绑定到 ifstream 对象
istream_iterator is(in_file);
istream_iterator eof;
//将 ostream_iterator 绑定到 ofstream 对象
ostream_iterator os(out_file, " ");
//...
}
#include
#include
#include
#include
#include
#include
using namespace std;
//标准库输入输出:屏幕输入屏幕输出实例(ctrl+z结束输入)
void test_iostream() {
istream_iterator is(cin);
istream_iterator eof;
vector text;
copy(is, eof, back_inserter(text));
sort(text.begin(), text.end());//给text里的元素排个序,(string容器按第一个元素的ASCII码排序)
ostream_iterator os(cout, " ");
copy(text.begin(), text.end(), os);
}
//文件中读写:先读入文件里的数据给一个容器(这里为string型),然后给容器里的元素排序,然后写入到另一个文件中。
void test_fstream() {
ifstream in_file("3.10Input.txt");
ofstream out_file("3.10Output.txt");
if (!in_file || !out_file) {
cerr << "!!unable to open the necessary files.\n"; //输出错误信息并清空输出缓存区
return -1;
}
istream_iteratoris(in_file);
istream_iteratoreof;
vector text;
copy(is, eof, back_inserter(text)); //复制操作,把文件里读取的数据扔给text这个容器
sort(text.begin(), text.end()); //给text里的元素排个序
ostream_iteratoros(out_file, " "); //定义os这个ostream_iterator并将其绑定至out_file这个文件输出(指针)
copy(text.begin(), text.end(), os); //复制操作,把text这个容器里的输入写入到文件中,每个写入的元素间有一个空格。
}
int main() {
// test_iostream();
test_fstream();
return 0;
}
当数组被传给函数或从函数中返回时,仅有第一个元素的地址会被传递。
下标操作[]
就是将起始地址加上索引值,产生出某个元素的地址,然后再提领该地址以返回元素值。
vector
和array
相同,都是以一块连续内存储存所有元素,但vector
可以为空,array
却不可以。所以操作vector
要先确定不为空。
typedef
机制(见第四章)让我们得以为任何一个类型提供另一个等价名称,通常用来简化那些声明起来十分麻烦的类型
typedef vector vstring;
每个标准容器都有一个begin()
操作函数,可返回一个泛型指针iterator
,指向第一个元素。end()
则指向最后一个元素的下一位置。
const_iterator
允许我们读取vector
元素,但不允许任何写入操作。
所有容器的共通操作:
- equality(
==
)和inequality(!=
)运算符,返回true
或false
- assignment(
=
)运算符,将某个容器复制给另一个容器empty()
会在容器无任何元素时返回true
,否则返回false
size()
返回容器内目前持有的元素个数clear()
删除所有元素begin()
返回一个iterator,指向容器的第一个元素end()
返回一个iterator,指向容器的最后一个元素的下一个位置insert()
将单一或某个范围内的元素插入容器内erase()
将容器内的单一元素或某个范围内的元素删除