chapter1 stl介绍
1 stl四个概念库
容器库:管理和存储数据的容器(数组链表,映射集合)
迭代器库(iterator头文件中)
算法库(algorithm头文件中)通用算法
数值库(数学函数+数值处理高级函数)
2 模板
从模板生成的函数或类的定义是模板的实例或实例化
可以以内联的方式为类模板的成员函数指定一个外部模板
template<typename T>
inline Array<T>::Array(cosnt Array& other)
try:elements{new T[other.count]},count{other.count}
{
for (size_t i = 0; i < count; i++)
{
elements[i] = other.elements[i];
}
}
catch(bad_alloc&)
{
cerr<<"memory allocation failed for Array object copy."<<endl;
}
每个类模板类型参数需要一个实参,除非有默认实参
Array<int> data{40};
编译上述 语句时,发生
- Array<int> 类的定义被创建,所以确定了参数的类型
- 因为必须调用构造器去生成一个对象,所以生成了构造器的定义
- 析构函数被用来销毁对象。
//模板别名
template<typename T> using ptr = shared_ptr<T>
//ptr<T> 作为 shared_ptr<T>的别名
3 容器
- 序列容器:以线性组织的方式存储对象,和数组类似,但不需要连续存储空间。
- 关联容器:存储了一些和键关联的对象。k-v
- 容器适配器:提供了替换机制的适配类模板。可以用来访问基础的序列容器和关联容器
所有的stl存储的都是对象的副本(除非对象是右值)
stl要求移动构造器和复制运算符都是noexcept
在存储基类指针或基类智能指针的容器中存储派生类指针(多态技术)而不要存储对象。
4 迭代器
结束迭代器表示一个容器中的所有元素,不会指向具体某个元素,所以不能解引用。
- 获取迭代器
begin();//方法,函数均可
end();
cbegin();//返回常量迭代器
cend();
一个 迭代器必须有一个拷贝构造函数,一个析构函数,一个拷贝赋值运算符
-
一个算法可以两种方式使用迭代器参数的类别:
- 为了满足操作,他会确立需要满足的最低限度要求
- 如果超出了迭代器的最低要求,算法使用扩展的功能可以更高效地执行运算
迭代器类别
-
输入迭代器:提供对象的只读访问(*iter)引用他所指向的值
- 包含操作 ++iter iter++ iter1==iter2 iter!=iter2
- 没有减量运算
- 自增后,只能通过使用新的迭代器来访问上一个他指向的元素
- iter->num来访问元素
-
输出迭代器:提供对象只写访问
- 包含操作 ++iter iter++
- 没有减量操作
- 每次想写一个序列元素时,都需要新创建一个新的迭代器
正向迭代器:结合了输入输出功能,可以使用多次(可以多次使用和读写)
双向迭代器:比正向迭代器多了一个iter--操作,可以向后遍历
-
随机访问迭代器:比双向迭代器多了随机访问的功能(支持很多操作)
iter+n iter-n iter+=n iter-=n
iter[n] *(iter+n)
iter1-iter2:两个迭代器之间元素的个数
iter1<iter2 iter1>iter2
一个随机访问迭代器可以像数组一样访问按下标访问元素
iter[3] ;//*(iter+3);//第四个元素
accumulate() 根据迭代器指针,计算容器中值的和(#include<numeric>)
案例
#include<iostream>
#include<numeric>
using namespace std;
int main()
{
double data[] {2.5, 4.5, 6.5, 5.5, 8.5};
cout<<"the array contains :"<<endl;
for(auto iter = begin(data);iter!=end(data);iter++)
{
cout<<*iter<<' ';
}
auto total = accumulate(begin(data),end(data),0.0);
//初始 末尾 sum的初始值(确保输入的类型要一致)
cout<<" the total of array is "<<total<<endl;
return 0;
}
流迭代器:
cout<<"enter an array number split with space"<<endl;
cout<<accumulate(istream_iterator<double>(cin),istream_iterator<double>(),0.0);
istream_iterator就是一个标准的流输入迭代器,可以是文件流,也可以是cin标准输入流
istream_iterator() 无参构造器可以创建一个结束标志(ctrl+z)
迭代器适配器
-
反向迭代器:与常规迭代器完全相反
- rbegin()指向最后一个元素
- rend()指向第一个元素之前
- ++表示从最后向前走一位
-
插入迭代器:不能别运用到array上
- 后向插入迭代器:push_back(),将元素添加到容器尾部
- vector
- list
- deque
- 前向插入迭代器:push_front(),将元素添加到容器头部
- list
- foword_list
- deque
- 插入迭代器:向任何有insert()函数的容器中插入新元素
- string
- 后向插入迭代器:push_back(),将元素添加到容器尾部
移动迭代器:将某个范围的类对象移动到目标范围,而不需要通过拷贝去移动。可以将移动迭代器作为输入迭代器,将所指向的对象转换为右值引用,如此,可以实现移动对象,而不是拷贝对象。
迭代器上的运算
- advance(iter,n); 相当于iter+3
- distance(begin(data),end(data)); 返回两个指针间元素的个数
- next(iter,n); 正向偏移(iter+3)
- prev(); 反向偏移(iter-3)
5 智能指针
智能指针只能用来保存堆上分配的内存的地址
不能自增自减
unique_ptr<T>
一个指向类型T的指针,排他(不可能有其他的unique_ptr<T>指向同一个地址)
但是当一个对象被一个unique_ptr<T>指向时,也可以通过生成一个原生指针来访问对象
//生成这种指针的最好的方式是使用make_unique<T>()函数 auto pname = make_unique<string>("hhw"); //通过解引用的方式来使用该对象 cout<<*pnanme<<endl; //指向数组: unique_ptr<int[]> pnumbers = make_unique<int[]>(10); //按索引访问 for(int i=0;i<10;i++) { pnumbers[i] = i*i; }
不能以传值的方式将一个unique_ptr<T> 对象传入函数中,因为他们不支持拷贝,必须使用引用的方式。
类的get成员函数可以返回一个unique_ptr<T>所包含的原生指针
auto unique_p = make_unique<string>(6,"*");
string pstr{unique_p.get()};
//当需要访问一个对象时,他的智能指针存储在一个类中
- 重置unique_ptr<T>对象
调用reset()之后,由unique_ptr<T>生成的原生指针会被置空,指向的地址空间被析构。
auto pname = make_unique<string>("hhe"); pname = reset();//释放string对象的内存 pname.reset(new string({"1234rt"})); //将之前指定的对象释放,内存中生成一个新的字符串,被pname保存
-
不要将其他unique_ptr<T>所指向的一个对象的地址传给reset(),或者去生成一个新的unique_ptr<T>对象
- 比如释放ptr1,指向ptr2
- 当ptr2不在使用时,会再次释放ptr1
可以调用release()方法去释放一个unique_ptr所指向的对象(可以在不释放对象内存的情况下,将指向他内部的原生指针置空
比较和检查unique_ptr<T>对象---就是比较两个对象get()函数返回的地址值
当对unique_ptr指针对象调用reset或者release时,需要先检查是否为空,解引用需要确保非空
shared_ptr<T>
用法大致和unique_ptr相同,不过shared_ptr 维护了一个控制块,用于记录当前有多少个shared_ptr指向当前地址空间,没增加一个,控制块会增加
使用成员函数get()可以获得一个原生指针
只能通过赋值运算符或者拷贝构造函数去复制一个shared_ptr对象,不可以使用一个shared_ptr的get()返回的指针来生成新的shared_ptr
-
重置shared_ptr对象
auto pname = make_shared<string>("hhw"); pname = nullptr; //cnt会减少 pname.reset();//同样效果,重置对象
reset()可以传入一个原生指针来改变共享指针指向的对象
pname.reset(new string("bjbf"));
- 比较和检查shared_ptr对象
- unique() //对象只有一个实例返回true,其他为false
- use_count() //返回当前被调用对象的实例个数
pname = nullptr;
if (pname.unique())
{
cout<<"only one "<<endl;
}
else{
cout<<pname.use_count()<<endl;
}
weak_ptr<T>
作用:
判断他所指向的对象是否存在(仍有共享指针指向他)
从一个weak_ptr中创建共享指针
//创建
auto pdata = make_shared<string>();
weak_ptr<string> pwdata{pdata};
weak_ptr<string> pwdata2{pwdata};
if (pwdata.expired())
{
//对象不存在
}
else{
//对象存在
}
5 函数对象
案例
重写operator() 即可实现自动调用
//BOX.H
#include<iostream>
class Box
{
private:
double length;
double height;
double width;
public:
Box(double lv,double hv,double wv);
~Box();
double volume() const {return length*height*width;}
double getLength() const{return length;}
double getHeight() const {return height;}
double getWidth() const {return width;}
};
Box::Box(double lv,double hv,double wv)
{
length = lv;
height = hv;
width = wv;
}
Box::~Box()
{
}
//VOLUME.H
#include"Box.h"
class Volume
{
private:
/* data */
public:
Volume(/* args */);
~Volume();
double operator()(double x,double y,double z){return x*y*z;}
double operator()(const Box& box){return box.getLength()*box.getHeight()*box.getWidth();}
};
//main()
#include"volume.h"
using namespace std;
int main()
{
Volume volume;
Box box{1,2,3};
cout<<volume(box)<<endl;
return 0;
}
lambda表达式传递给函数
因为类型不明确,所以要建立一个接受lambda表达式为参数的函数模板
案例:
#include<iostream>
using namespace std;
//接受lambda表达式为参数的函数模板F fun
template<typename ForwardIter,typename F>
void change(ForwardIter begin,ForwardIter end,F fun)
{
for(auto iter=begin;iter!=end;iter++)
{
*iter = fun(*iter);
}
}
int main()
{
int data[]{1,2,3,4,5,6,7,8};
for (auto &&i : data)
{
cout<<i<<' ';
}
cout<<endl;
//传入lambda表达式
change(begin(data),end(data),[](int value){
return value*value;
});
for (auto &i : data)
{
cout<<i<<' ';
}
return 0;
}
标准库的function头文件定义了一个模板类型的functional<> 这是对任意类型函数指针的封装,有给定的返回类型和形参类型
//对任意类型函数指针的封装
functional<double(double)> op {
[](double value){return value*value*value;}
};
- generate(be,end,lambda)函数模板:
用函数对象计算值,初始化一段元素
unsigned int height{};
//lambda每次调用都会增加height
generate{
begin(data),end(data),
[height,&min_ht,&ht_step]()mutable
{return height+=height==0?min_ht:ht_step;}
};
- itoa(be,end,value)函数模板
只要支持operator++()的类型,都可以使用此方法进行连续递增初始化,第一个元素从value开始
array<double,10> values;
iota(begin(values),end(values),10.0);
//values数值为 10 11 12 13 ----- 19.0
- transform(be,end,指定结果存放起始位置的迭代器,应用到输入序列的函数)
该算法将be->end的所有元素应用fun操作,存储值新的迭代器中
transform(
begin(),end(),
ostream_iterator<double>(cout,""),
[]double x {return x*x;}
);