stl概述

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ā)生

  1. Array<int> 類的定義被創(chuàng)建,所以確定了參數(shù)的類型
  2. 因為必須調用構造器去生成一個對象,所以生成了構造器的定義
  3. 析構函數(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 迭代器

結束迭代器表示一個容器中的所有元素,不會指向具體某個元素,所以不能解引用。

  1. 獲取迭代器
begin();//方法,函數(shù)均可
end();
cbegin();//返回常量迭代器
cend();
  1. 一個 迭代器必須有一個拷貝構造函數(shù),一個析構函數(shù),一個拷貝賦值運算符

  2. 一個算法可以兩種方式使用迭代器參數(shù)的類別:

    1. 為了滿足操作,他會確立需要滿足的最低限度要求
    2. 如果超出了迭代器的最低要求,算法使用擴展的功能可以更高效地執(zhí)行運算

迭代器類別

  1. 輸入迭代器:提供對象的只讀訪問(*iter)引用他所指向的值

    1. 包含操作 ++iter iter++ iter1==iter2 iter!=iter2
    2. 沒有減量運算
    3. 自增后,只能通過使用新的迭代器來訪問上一個他指向的元素
    4. iter->num來訪問元素
  2. 輸出迭代器:提供對象只寫訪問

    1. 包含操作 ++iter iter++
    2. 沒有減量操作
    3. 每次想寫一個序列元素時,都需要新創(chuàng)建一個新的迭代器
  3. 正向迭代器:結合了輸入輸出功能,可以使用多次(可以多次使用和讀寫)

  4. 雙向迭代器:比正向迭代器多了一個iter--操作,可以向后遍歷

  5. 隨機訪問迭代器:比雙向迭代器多了隨機訪問的功能(支持很多操作)

    1. iter+n iter-n iter+=n iter-=n

    2. iter[n] *(iter+n)

    3. iter1-iter2:兩個迭代器之間元素的個數(shù)

    4. 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)

迭代器適配器

  1. 反向迭代器:與常規(guī)迭代器完全相反

    1. rbegin()指向最后一個元素
    2. rend()指向第一個元素之前
    3. ++表示從最后向前走一位
  2. 插入迭代器:不能別運用到array上

    1. 后向插入迭代器:push_back(),將元素添加到容器尾部
      1. vector
      2. list
      3. deque
    2. 前向插入迭代器:push_front(),將元素添加到容器頭部
      1. list
      2. foword_list
      3. deque
    3. 插入迭代器:向任何有insert()函數(shù)的容器中插入新元素
      1. string
  3. 移動迭代器:將某個范圍的類對象移動到目標范圍,而不需要通過拷貝去移動??梢詫⒁苿拥髯鳛檩斎氲?,將所指向的對象轉換為右值引用,如此,可以實現(xiàn)移動對象,而不是拷貝對象。

迭代器上的運算

  1. advance(iter,n); 相當于iter+3
  2. distance(begin(data),end(data)); 返回兩個指針間元素的個數(shù)
  3. next(iter,n); 正向偏移(iter+3)
  4. 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()};
//當需要訪問一個對象時,他的智能指針存儲在一個類中
  1. 重置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對象
  1. unique() //對象只有一個實例返回true,其他為false
  2. 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;}
};

  1. 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;}
    };
  1. 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
  1. transform(be,end,指定結果存放起始位置的迭代器,應用到輸入序列的函數(shù))

該算法將be->end的所有元素應用fun操作,存儲值新的迭代器中

transform(
    begin(),end(),
    ostream_iterator<double>(cout,""),
    []double x {return x*x;}
);
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 本節(jié)我們將介紹 C++ STL 中智能指針的使用。 智能指針(英語:Smart pointer)是一種抽象的數(shù)據類...
    思想永不平凡閱讀 2,946評論 0 2
  • 2. C++標準庫 2.1 IO庫 IO對象無拷貝或賦值,進行IO操作的函數(shù)通常以引用方式傳遞和返回流。 IO庫條...
    王偵閱讀 1,601評論 0 0
  • MultiThread是自己在B站學習C++多線程,跟老師寫的代碼b站up主mkuangxianghttps://...
    南_橘子豬閱讀 603評論 0 0
  • 概要:本文是個人零碎時間整理的一些知識點,涉及Linux和C++。比較零碎,在此記錄一下。 03/18/2018 ...
    小碼弟閱讀 379評論 0 0
  • 抱佛腳一時爽,一直抱佛腳一直爽!這篇文章總結常見的c++面試問題~因為是抱佛腳,所以結構上沒有什么邏輯...參考鏈...
    山幺幺閱讀 807評論 0 0

友情鏈接更多精彩內容