chapter1 stl介紹
1 stl四個概念庫
容器庫:管理和存儲數(shù)據的容器(數(shù)組鏈表,映射集合)
迭代器庫(iterator頭文件中)
算法庫(algorithm頭文件中)通用算法
數(shù)值庫(數(shù)學函數(shù)+數(shù)值處理高級函數(shù))
2 模板
從模板生成的函數(shù)或類的定義是模板的實例或實例化
可以以內聯(lián)的方式為類模板的成員函數(shù)指定一個外部模板
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;
}
每個類模板類型參數(shù)需要一個實參,除非有默認實參
Array<int> data{40};
編譯上述 語句時,發(fā)生
- Array<int> 類的定義被創(chuàng)建,所以確定了參數(shù)的類型
- 因為必須調用構造器去生成一個對象,所以生成了構造器的定義
- 析構函數(shù)被用來銷毀對象。
//模板別名
template<typename T> using ptr = shared_ptr<T>
//ptr<T> 作為 shared_ptr<T>的別名
3 容器
- 序列容器:以線性組織的方式存儲對象,和數(shù)組類似,但不需要連續(xù)存儲空間。
- 關聯(lián)容器:存儲了一些和鍵關聯(lián)的對象。k-v
- 容器適配器:提供了替換機制的適配類模板。可以用來訪問基礎的序列容器和關聯(lián)容器
所有的stl存儲的都是對象的副本(除非對象是右值)
stl要求移動構造器和復制運算符都是noexcept
在存儲基類指針或基類智能指針的容器中存儲派生類指針(多態(tài)技術)而不要存儲對象。
4 迭代器
結束迭代器表示一個容器中的所有元素,不會指向具體某個元素,所以不能解引用。
- 獲取迭代器
begin();//方法,函數(shù)均可
end();
cbegin();//返回常量迭代器
cend();
一個 迭代器必須有一個拷貝構造函數(shù),一個析構函數(shù),一個拷貝賦值運算符
-
一個算法可以兩種方式使用迭代器參數(shù)的類別:
- 為了滿足操作,他會確立需要滿足的最低限度要求
- 如果超出了迭代器的最低要求,算法使用擴展的功能可以更高效地執(zhí)行運算
迭代器類別
-
輸入迭代器:提供對象的只讀訪問(*iter)引用他所指向的值
- 包含操作 ++iter iter++ iter1==iter2 iter!=iter2
- 沒有減量運算
- 自增后,只能通過使用新的迭代器來訪問上一個他指向的元素
- iter->num來訪問元素
-
輸出迭代器:提供對象只寫訪問
- 包含操作 ++iter iter++
- 沒有減量操作
- 每次想寫一個序列元素時,都需要新創(chuàng)建一個新的迭代器
正向迭代器:結合了輸入輸出功能,可以使用多次(可以多次使用和讀寫)
雙向迭代器:比正向迭代器多了一個iter--操作,可以向后遍歷
-
隨機訪問迭代器:比雙向迭代器多了隨機訪問的功能(支持很多操作)
iter+n iter-n iter+=n iter-=n
iter[n] *(iter+n)
iter1-iter2:兩個迭代器之間元素的個數(shù)
iter1<iter2 iter1>iter2
一個隨機訪問迭代器可以像數(shù)組一樣訪問按下標訪問元素
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() 無參構造器可以創(chuàng)建一個結束標志(ctrl+z)
迭代器適配器
-
反向迭代器:與常規(guī)迭代器完全相反
- rbegin()指向最后一個元素
- rend()指向第一個元素之前
- ++表示從最后向前走一位
-
插入迭代器:不能別運用到array上
- 后向插入迭代器:push_back(),將元素添加到容器尾部
- vector
- list
- deque
- 前向插入迭代器:push_front(),將元素添加到容器頭部
- list
- foword_list
- deque
- 插入迭代器:向任何有insert()函數(shù)的容器中插入新元素
- string
- 后向插入迭代器:push_back(),將元素添加到容器尾部
移動迭代器:將某個范圍的類對象移動到目標范圍,而不需要通過拷貝去移動??梢詫⒁苿拥髯鳛檩斎氲?,將所指向的對象轉換為右值引用,如此,可以實現(xiàn)移動對象,而不是拷貝對象。
迭代器上的運算
- advance(iter,n); 相當于iter+3
- distance(begin(data),end(data)); 返回兩個指針間元素的個數(shù)
- next(iter,n); 正向偏移(iter+3)
- prev(); 反向偏移(iter-3)
5 智能指針
智能指針只能用來保存堆上分配的內存的地址
不能自增自減
unique_ptr<T>
一個指向類型T的指針,排他(不可能有其他的unique_ptr<T>指向同一個地址)
但是當一個對象被一個unique_ptr<T>指向時,也可以通過生成一個原生指針來訪問對象
//生成這種指針的最好的方式是使用make_unique<T>()函數(shù) auto pname = make_unique<string>("hhw"); //通過解引用的方式來使用該對象 cout<<*pnanme<<endl; //指向數(shù)組: unique_ptr<int[]> pnumbers = make_unique<int[]>(10); //按索引訪問 for(int i=0;i<10;i++) { pnumbers[i] = i*i; }不能以傳值的方式將一個unique_ptr<T> 對象傳入函數(shù)中,因為他們不支持拷貝,必須使用引用的方式。
類的get成員函數(shù)可以返回一個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()函數(shù)返回的地址值
當對unique_ptr指針對象調用reset或者release時,需要先檢查是否為空,解引用需要確保非空
shared_ptr<T>
用法大致和unique_ptr相同,不過shared_ptr 維護了一個控制塊,用于記錄當前有多少個shared_ptr指向當前地址空間,沒增加一個,控制塊會增加
使用成員函數(shù)get()可以獲得一個原生指針
只能通過賦值運算符或者拷貝構造函數(shù)去復制一個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() //返回當前被調用對象的實例個數(shù)
pname = nullptr;
if (pname.unique())
{
cout<<"only one "<<endl;
}
else{
cout<<pname.use_count()<<endl;
}
weak_ptr<T>
作用:
判斷他所指向的對象是否存在(仍有共享指針指向他)
從一個weak_ptr中創(chuàng)建共享指針
//創(chuàng)建
auto pdata = make_shared<string>();
weak_ptr<string> pwdata{pdata};
weak_ptr<string> pwdata2{pwdata};
if (pwdata.expired())
{
//對象不存在
}
else{
//對象存在
}
5 函數(shù)對象
案例
重寫operator() 即可實現(xiàn)自動調用
//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表達式傳遞給函數(shù)
因為類型不明確,所以要建立一個接受lambda表達式為參數(shù)的函數(shù)模板
案例:
#include<iostream>
using namespace std;
//接受lambda表達式為參數(shù)的函數(shù)模板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<> 這是對任意類型函數(shù)指針的封裝,有給定的返回類型和形參類型
//對任意類型函數(shù)指針的封裝
functional<double(double)> op {
[](double value){return value*value*value;}
};
- generate(be,end,lambda)函數(shù)模板:
用函數(shù)對象計算值,初始化一段元素
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)函數(shù)模板
只要支持operator++()的類型,都可以使用此方法進行連續(xù)遞增初始化,第一個元素從value開始
array<double,10> values;
iota(begin(values),end(values),10.0);
//values數(shù)值為 10 11 12 13 ----- 19.0
- transform(be,end,指定結果存放起始位置的迭代器,應用到輸入序列的函數(shù))
該算法將be->end的所有元素應用fun操作,存儲值新的迭代器中
transform(
begin(),end(),
ostream_iterator<double>(cout,""),
[]double x {return x*x;}
);