主頁 > 後端開發 > 《C++ Primer》筆記 第10章 泛型演算法

《C++ Primer》筆記 第10章 泛型演算法

2021-02-01 06:08:35 後端開發

  1. 迭代器令演算法不依賴于容器,但演算法依賴于元素型別的操作,

  2. 演算法永遠不會執行容器的操作,演算法永遠不會改變底層容器的大小,

  3. accumulate定義在頭檔案numeric中,接受三個引數,前兩個指出需要求和的元素的范圍,第三個引數是和的初值,accumulate的第三個引數的型別決定了函式中使用哪個加法運算子以及回傳值的型別,

    // 對vec中的元素求和,和的初值是0
    int sum = accumulate(vec.cbegin(), vec.cend(), 0);
    
    // 將vector中所有string元素連接起來
    string sum = accumulate(v.cbegin(), v.cend(), string(""));
    // 錯誤:const char*上沒有定義+運算子
    string sum = accumulate(v.cbegin(), v.cend(), "");
    
  4. 對于只讀取而不改變元素的演算法,通常最好使用cbegin()和cend(),但是,如果你計劃使用演算法回傳的迭代器來改變元素的值,就需要使用begin()和end()的結果作為引數,

  5. equal用于確定兩個序列是否保存相同的值,它將第一個序列中的每個元素與第二個序列中的對應元素進行比較,如果所有對應元素都相等,則回傳true,否則回傳false,此演算法接受三個迭代器:前兩個表示第一個序列中的元素范圍,第三個表示第二個序列的首元素,

  6. 那些只接受一個單一迭代器來表示第二個序列的演算法,都假定第二個序列至少與第一個序列一樣長,

  7. 演算法fill接受一對迭代器表示一個范圍,還接受一個值作為第三個引數,fill將給定的這個值賦予輸入序列中的每個元素,

  8. 一些演算法從兩個序列中讀取元素,構成這兩個序列的元素可以來自于不同型別的容器,而且,兩個序列中元素的型別也不要求嚴格匹配,演算法要求的只是能夠比較兩個序列中的元素,

  9. 函式fill_n接受一個單迭代器、一個計數值和一個值,它將給定值賦予迭代器指向的元素開始的指定個元素,示例:fill_n(dest, n, val),fill_n假定dest指向一個元素,而從dest開始的序列至少包含n個元素,

  10. 向目的位置迭代器寫入資料的演算法假定目的位置足夠大,能容納要寫入的元素,

  11. 插入迭代器是一種像容器中添加元素的迭代器,通常情況,當我們通過一個迭代器向容器元素賦值時,值被賦予迭代器指向的元素,而當我們通過一個插入迭代器賦值時,一個與賦值號右側值相等的元素被添加到容器中,

  12. back_inserter定義在頭檔案iterator中,接受一個指向容器的參考,回傳一個與該容器系結的插入迭代器,當我們通過此迭代器賦值時,賦值運算子會呼叫push_back將一個具有給定值的元素添加到容器中:

    vector<int> vec; // 空向量
    auto it = back_inserter(vec); // 通過它賦值會將元素添加到vec中
    *it = 42; // vec中現在有一個元素,值為42
    
    // 我們常常使用back_inserter來創建一個迭代器,作為演算法的目的位置來使用
    vector<int> vec; // 空向量
    // 正確:back_inserter創建一個插入迭代器,可用來向vec添加元素
    fill_n(back_inserter(vec), 10, 0); // 添加10個元素到vec
    
  13. 拷貝演算法copy接受三個迭代器,前兩個表示一個輸入范圍,第三個表示目的序列的起始位置,此演算法將輸入范圍中的元素拷貝到目的序列中,傳遞給copy的目的序列至少要包含與輸入序列一樣多的元素,

    // 利用copy實作內置陣列的拷貝
    int a1[] = {0,1,2,3,4,5,6,7,8,9};
    int a2[sizeof(a1)/sizeof(*a1)]; // a2與a1大小一樣
    // ret指向拷貝到a2的尾元素之后的位置
    auto ret = copy(begin(a1), end(a1), a2); // 把a1的內容拷貝給a2
    
  14. copy回傳的是其目的位置迭代器(遞增后)的值,即,ret恰好指向拷貝到a2的尾元素之后的位置,

  15. replace演算法讀入一個序列,并將其中所有等于給定值的元素都改為另一個值,此演算法接受4個引數:前兩個是迭代器,表示輸入序列,后兩個一個是要搜索的值,另一個是新值,它將所有等于第一個值的元素替換為第二個值:

    // 將所有值為0的元素改為42
    replace(ilst.begin(), ilst.end(), 0, 42);
    
  16. 如果我們希望保留原序列不變,可以呼叫replace_copy,此演算法接受額外第三個迭代器引數,指出調整后序列的保存位置:

    // 使用back_inserter按需要增長目的序列
    replace_copy(ilst.cbegin(), ilst.cend(), back_inserter(ivec), 0, 42);
    
  17. sort演算法接受兩個迭代器,表示要排序的元素范圍,

  18. unique演算法重排輸入序列,將相鄰的重復項“消除”,并回傳一個指向不重復值范圍的末尾的迭代器,unique回傳的迭代器指向最后一個不重復元素之后的位置,此位置之后的元素仍然存在,但我們不知道它們的值是什么,

  19. 標準庫演算法對迭代器而不是容器進行操作,因此,演算法不能(直接)添加或洗掉元素,

    void elimDups(vector<string> &words)
    {
        // 按字典序排序words,以便查找重復單詞
        sort(words.begin(), words.end());
        // unique重排輸入范圍,使得每個單詞只出現一次
        // 排列在范圍的前部,回傳指向不重復區域之后一個位置的迭代器
        auto end_unique = unique(words.begin(), words.end());
        // 使用向量操作erase洗掉重復單詞
        words.erase(end_unique, words.end());
    }
    
  20. 謂詞是一個可呼叫的運算式,其回傳結果是一個能用作條件的值,標準庫演算法所使用的謂詞分為兩類:一元謂詞(意味著它們只接受單一引數)和二元謂詞(意味著它們有兩個引數),接受謂詞引數的演算法對輸入序列中的元素呼叫謂詞,因此,元素型別必須能轉換為謂詞的引數型別,

    // 比較函式,用來按長度排序單詞
    bool isShorter(const string &s1, const string &s2)
    {
    	return s1.size() < s2.size();
    }
    
  21. 穩定排序演算法stable_sort維持相等元素的原有順序,假定在此呼叫前words是按字典序排列的,則呼叫之后,words會按元素大小排序,而長度相同的單詞會保持字典序:

    elimDups(words); // 將words按字典序重排,并消除重復單詞
    // 按長度重新排序,長度相同的單詞維持字典序
    stable_sort(words.begin(), words.end(), isShorter);
    for(const auto &s : words) // 無需拷貝字串
    	cout << s << " "; // 列印每個元素,以空格分隔
    cout << endl;
    
  22. 標準庫定義了名為partition的演算法,它接受一個謂詞,對容器內容進行劃分,使得謂詞為true的值會排在容器的前半部分,而使謂詞為false的值會排在后半部分,演算法回傳一個迭代器,指向最后一個使謂詞為true的元素之后的位置,

  23. find_if演算法接受一對迭代器,表示一個范圍,第三個引數是一個謂詞,find_if演算法對輸入序列中的每個元素呼叫給定的這個謂詞,它回傳第一個使謂詞回傳非0值的元素,如果不存在這樣的元素,則回傳尾后迭代器,

    // 獲取一個迭代器,指向第一個滿足size() >= sz的元素
    auto wc = find_if(
    		words.begin(),
    		words.end(), 
    		[sz](const string &a){ return a.size() >= sz; });
    
  24. 對于一個物件或一個運算式,如果可以對其使用呼叫運算子,則稱它為可呼叫的,包括函式、函式指標、多載了函式呼叫運算子的類和lambda運算式,

  25. 一個lambda運算式表示一個可呼叫的代碼單元,我們可以將其理解為一個未命名的行內函式,lambda可能定義在函式內部,lambda必須使用尾置回傳來指定回傳型別:

    [capture list](parameter list) -> return type { function body }
    
  26. 我們可以忽略引數串列和回傳型別,但必須永遠包含捕獲串列和函式體,在lambda中忽略括號和引數串列等價于指定一個空引數串列,如果忽略回傳型別,lambda根據函式體中的代碼推斷出回傳型別,如果函式體只是一個return陳述句,則回傳型別從回傳的運算式的型別推斷而來,否則回傳型別為void,

  27. lambda的呼叫方式與普通函式的呼叫方式相同,都是使用呼叫運算子,

  28. lambda不能有默認引數,因此,一個lambda呼叫的實引數目永遠與形引數目相等,

  29. 一個lambda只有在其捕獲串列中捕獲一個它所在函式中的區域變數,才能在函式體中使用該變數,

  30. for_each演算法接受一個可呼叫物件,并對輸入序列中每個元素呼叫此物件:

    // 列印長度大于等于給定值的單詞,每個單詞后面接一個空格
    for_each(wc, words.end(), [](const string &s){ cout << s << " "; });
    cout << endl;
    
  31. 捕獲串列只用于區域非static變數,lambda可以直接使用區域static變數和在它所在函式之外宣告的名字,

  32. 當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)型別別,默認情況下,從lambda生成的類都包含一個對應該lambda所捕獲的變數的資料成員,類似任何普通類的資料成員,lambda的資料成員也在lambda物件創建時被初始化,

  33. 類似引數傳遞,變數的捕獲方式也可以是值或參考,

    • 采用值捕獲的前提是變數可以拷貝,與引數不同,被捕獲的變數的值是在lambda創建時拷貝,而不是呼叫時拷貝:

      void fcn1()
      {
      	size_t v1 = 42; // 區域變數
      	// 將v1拷貝到名為f的可呼叫物件
      	auto f = [v1] { return v1; };
      	v1 = 0;
      	auto j = f(); // j為42;f保存了我們創建它時v1的拷貝
      }
      
    • 采用參考方式捕獲變數

      void fcn2()
      {
      	size_t v1 = 42; // 區域變數
      	// 物件f2包含v1的參考
      	auto f2 = [&v1]{ return v1; };
      	v1 = 0;
      	auto j = f2(); // j為0;f2保存v1的參考,而非拷貝
      }
      
      • 參考有時是必要的:
      void biggies(vector<string> &words,
      			vector<string>::size_type sz,
      			ostream &os = cout, char c = ' ')
      {
      	// 與之前例子一樣的重排words的代碼
      	// 列印count的陳述句改為列印到os
      	for_each(words.begin(), words.end(),
      			[&os, c](const string &s){ os << s << c; });
      }
      
      • 如果函式回傳一個lambda,則與函式不能回傳一個區域變數的參考類似,此lambda也不能包含參考捕獲,
      • 當以參考方式捕獲一個變數時,必須保證在lambda執行時變數是存在的,
  34. 隱式捕獲:為了指示編譯器推斷捕獲串列,應在捕獲串列中寫一個&或=,&告訴編譯器采用捕獲參考方式,=則表示采用值捕獲方式,

    // sz為隱式捕獲,值捕獲方式
    wc = find_if(words.begin(), words.end(),
    			[=](const string &s){ return s.size() >= sz; });
    

    可以混合使用隱式捕獲和顯式捕獲:

    void biggies(vector<string> &words,
    			vector<string>::size_type sz,
    			ostream &os = cout, char c = ' ')
    {
    	// 其他處理與前例一樣
    	// os隱式捕獲,參考捕獲方式;c顯示捕獲,值捕獲方式
    	for_each(words.begin(), words.end(), 
    			[&, c](const string &s){ os << s << c; });
    	// os顯式捕獲,參考捕獲方式;c隱式捕獲,值捕獲方式
    	for_each(words.begin(), words.end(), 
    			[=, &os](const string &s){ os << s << c; });
    }
    
    lambda捕獲串列 解釋
    [] 空捕獲串列,lambda不能使用所在函式中的變數,一個lambda只有捕獲變數后才能使用它們
    [names] names是一個逗號分隔的名字串列,這些名字都是lambda所在函式的區域變數,默認情況下,捕獲串列中的變數都被拷貝,名字前如果使用了&,則采用參考捕獲方式
    [&] 隱式捕獲串列,采用參考捕獲方式,lambda體中所使用的來自所在函式的物體都采用參考方式使用
    [=] 隱式捕獲串列,采用值捕獲方式,lambda體將拷貝所使用的來自所在函式的物體的值
    [&, identifier_list] identifier_list是一個逗號分隔的串列,包含0個或多個來自所在函式的變數,這些變數采用值捕獲方式,而任何隱式捕獲的變數都采用參考的方式捕獲,identifier_list中的名字前面不能使用&
    [=, identifier_list] identifier_list中的變數都采用參考方式捕獲,而任何隱式捕獲的變數都采用值方式捕獲,identifier_list中的名字不能包括this,且這些名字之前必須使用&
  35. 默認情況下,對于一個值被拷貝的變數,lambda不會改變其值,如果我們希望能改變一個被捕獲的變數的值,就必須在引數串列后加上關鍵字mutable,因此,可變lambda不能省略引數串列:

    void fcn3()
    {
    	size_t v1 = 42; // 區域變數
    	// f可以改變它所捕獲的變數的值
    	auto f = [v1] () mutable { return ++v1; }; // 不會影響外層v1的值
        v1 = 0;
        auto j = f(); // j為43 
        // 多次呼叫f,lambda中v1的值會累加
        // 在lambda中如果不加mutable,則v1是read-only的
    }
    

    一個參考捕獲的變數是否(如往常一樣)可以修改依賴于此參考指向的是一個const型別還是一個非const型別:

    void fcn4()
    {
    	size_t v1 = 42; // 區域變數
    	// v1是一個非const變數的參考
    	// 可以通過f2中的參考來改變它
    	auto f2 = [&v1]{ return ++v1; }; // 外層v1值改變
    	v1 = 0;
    	auto j = f2();
    }
    
  36. transform接受三個迭代器和一個可呼叫物件,前兩個迭代器表示輸入序列,第三個迭代器表示目的位置,演算法對輸入序列中每個元素呼叫可呼叫物件,并將結果寫到目的位置,目的位置迭代器與表示輸入序列開始位置的迭代器可以是相同的,當輸入迭代器和目的迭代器相同時,transform將輸入序列中每個元素替換為可呼叫物件操作該元素得到的結果,

    transform(vi.begin(), vi.end(), vi.begin(),
    		[](int i){ return i < 0 ? -i : i; });
    
  37. count_if演算法接受一對迭代器,表示一個輸入范圍,還接受一個謂詞,會對輸入范圍中每個元素執行,count_if回傳一個計數值,表示謂詞有多少次為真,

  38. 在很多地方使用相同的操作,或者一個操作需要很多陳述句才能完成,通常使用函式比lambda運算式更好,

  39. bind標準庫函式定義在頭檔案functional中,可以將bind函式看作一個通用的函式配接器,它接受一個可呼叫物件,生成一個新的可呼叫物件來“適應”原物件的引數串列,

  40. 呼叫bind的一般形式為:auto newCallable = bind(callable, arg_list);其中,newCallable本身是一個可呼叫物件,arg_list是一個逗號分隔的引數串列,對應給定的callable的引數,即,當我們呼叫newCallable時,newCallable會呼叫callable,并傳遞給它arg_list中的引數,

  41. arg_list中的引數可能包含形如_n的名字,其中n是一個整數,這些引數是”占位符“,表示newCallable的引數,它們占據了傳遞給newCallable的引數的”位置“,數值n表示生成的可呼叫物件中引數的位置:_1為newCallable的第一個引數,_2為第二個引數,以此類推,

    // g是一個有兩個引數的可呼叫物件
    auto g = bind(f, a, b, _2, c, _1); // 呼叫g(X,Y)會呼叫 f(a, b, Y, c, X)
    // 可用bind重排引數順序
    
  42. 名字_n都定義在一個名為placeholders的命名空間中,而這個命名空間本身定義在std命名空間中,using namespace std::placeholders;

  43. 默認情況下,bind的那些不是占位符的引數被拷貝到bind回傳的可呼叫物件中,但是,與lambda類似,有時對有些系結的引數我們希望以參考方式傳遞,或是要系結引數的型別無法拷貝,就必須使用標準庫ref函式:

    // 1
    for_each(words.begin(), words.end(), [&os, c](const string &s){ os << s << c; });
    // 2
    ostream &print(ostream &os, const string &s, char c)
    {
    	return os << s << c;
    }
    for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));
    
  44. 函式ref回傳一個物件,包含給定的參考,此物件是可以拷貝的,標準庫中還有一個cref函式,生成一個保存const參考的類,與bind一樣,函式ref和cref也定義在頭檔案functional中,

插入迭代器操作 解釋
it=t 在it指定的當前位置插入值t,假定c是it系結的容器,依賴于插入迭代器的不同種類,此賦值會分別呼叫c.push_back(t)、c.push_front(t)或c.insert(t,p),其中p為傳遞給inserter的迭代器位置
*it,++it,it++ 這些操作雖然存在,但不會對it做任何事情,每個操作都回傳it(所以*it++ = t;等價于it = t;
  1. 插入器有三種型別,差異在于元素插入的位置:

    • back_inserter創建一個使用push_back的迭代器
    • front_inserter創建一個使用push_front的迭代器
    • inserter創建一個使用insert的迭代器,此函式接受第二個引數,這個引數必須是一個指向給定容器的迭代器,元素將被插入到給定迭代器所表示的元素之前,
  2. 只有在容器支持push_front的情況下,我們才可以使用front_inserter,類似的,只有在容器支持push_back的情況下,我們才能使用back_inserter,

  3. 當呼叫inserter(c, iter)時,我們得到一個迭代器,接下來使用它時,會將元素插入到iter原來所指向的元素之前的位置,即,如果it是由inserter生成的迭代器,則下面這樣的賦值陳述句

    *it = val;
    

    其效果與下面代碼一樣:

    it = c.insert(it, val); // it指向新加入的元素
    ++it; // 遞增it使它指向原來的元素
    
  4. unique_copy函式接受第三個迭代器,表示拷貝不重復元素的目的位置,與unique一樣,要求重復元素相鄰存放,

  5. 對于一個系結到流的迭代器,一旦其關聯的流遇到檔案尾或遇到IO錯誤,迭代器的值就與尾后迭代器相等,

    istream_iterator<int> in_iter(cin), eof; // 從cin讀取int
    vector<int> vec(in_iter, eof); // 從迭代器范圍構造vec
    
istream_iterator操作 解釋
istream_iterator in(is); in從輸入流is讀取型別為T的值
istream_iterator end; 讀取型別為T的值的istream_iterator迭代器,表示尾后位置
in1 == in2 in1和in2必須讀取相同型別,如果它們都是尾后迭代器,或系結到相同的輸入,則兩者相等
*in 回傳從流中讀取的值
in->mem 與(*in).mem的含義相同
++in, in++ 使用元素型別所定義的>>運算子從輸入流中讀取下一個值,與以往一樣,前置版本回傳一個指向遞增后迭代器的參考,后置版本回傳舊值
  1. istream_iterator允許使用懶惰求值:標準庫并不保證迭代器立即從流讀取資料,具體實作可以推遲從流中讀取資料,直到我們使用迭代器時才真正讀取,標準庫中的實作所保證的是,在我們第一次解參考迭代器之前,從流中讀取資料的操作已經完成了,
ostream_iterator操作 解釋
ostream_iterator out(os); out將型別為T的值寫到輸出流os中
ostream_iterator out(os, d); out將型別為T的值寫到輸出流os中,每個值后面都輸出一個d,d指向一個空字符結尾的字符陣列
out = val 用<<運算子將val寫入到out所系結的ostream中,val的型別必須與out可寫的型別兼容
*out, ++out, out++ 這些運算子時存在的,但不對out做任何事情,每個運算子都回傳out
  1. 我們可以用ostream_iterator來輸出值的序列:

    ostream_iterator<int> out_iter(cout, " ");
    for(auto e : vec)
    	*out_iter++ = e; // 賦值陳述句實際上將元素寫到cout
    cout << endl;
    

    當我們向out_iter賦值時,可以忽略解參考和遞增運算,

    for(auto e : vec)
    	out_iter = e; // 賦值陳述句將元素寫到cout
    cout << endl;
    

    通過呼叫copy來列印vec中的元素,這比撰寫回圈更為簡單:

    copy(vec.begin(), vec.end(), out_iter);
    cout << endl;
    
  2. 我們可以為任何定義了輸入運算子(>>)的型別創建istream_iterator物件,類似的,只要型別有輸出運算子(<<),我們就可以為其定義ostream_iterator,

  3. 除了forward_list之外,其他容器都支持反向迭代器,我們可以通過呼叫rbegin、rend、crbegin、crend成員函式來獲得反向迭代器,這些成員函式回傳指向容器尾元素和首元素之前一個位置的迭代器,與普通迭代器一樣,反向迭代器也有const和非const版本,

  4. 通過向sort傳遞一對反向迭代器來將vector整理為遞減序:

    sort(vec.begin(), vec.end()); // 按“正常序”排序vec
    // 按逆序排序:將最小元素放在vec的末尾
    sort(vec.rbegin(), vec.rend());
    
  5. 只能從即支持++也支持--的迭代器來定義反向迭代器,流迭代器不支持遞減運算,因為不可能在一個流中反向移動,因此,不可能從一個forward_list或一個流迭代器創建反向迭代器,

  6. reverse_iteratorbase成員函式將反向迭代器轉換并回傳一個普通迭代器,

    // 在一個逗號分隔的串列中查找第一個元素
    auto comma = find(line.cbegin(), line.cend(), ',');
    cout << string(line.cbegin(), comma) << endl;
    
    // 在一個逗號分隔的串列中查找最后一個元素
    auto rcomma = find(line.crbegin(), line.crend(), ',');
    
    // 錯誤:將逆序輸出單詞的字符
    cout << string(line.crbegin(), rcomma) << endl;
    
    // 正確:得到一個正向迭代器,從逗號后開始讀取字符直到line末尾
    cout << string(rcomma.base(), line.cend()) << endl;
    
  7. 反向迭代器的目的是表示元素范圍,而這些范圍是不對稱的,這導致一個重要的結果:當我們從一個普通迭代器初始化一個反向迭代器,或是給一個反向迭代器賦值時,結果迭代器與原迭代器指向的并不是相同的元素,

  8. 迭代器類別:

    • 輸入迭代器:只讀,不寫;單邊掃描,只能遞增(++);還支持相等性判定運算子(==、!=)、解參考運算子(*)(只出現在賦值運算子右側)和箭頭運算子(->),
      • 輸入迭代器只用于順序訪問,
      • 對于一個輸入迭代器,*it++保證是有效的,但遞增它可能導致所有其他指向流的迭代器失效,其結果就是,不能保證輸入迭代器的狀態可以保存下來并用來訪問元素,因此,輸入迭代器只能用于單邊掃描演算法,
      • 演算法find和accumulate要求輸入迭代器;而istream_iterator是一種輸入迭代器,
    • 輸出迭代器:只寫,不讀;單邊掃描,只能遞增(++),支持解參考運算子(*)(只出現在賦值運算子左側),
      • 我們只能向一個輸出迭代器賦值一次,
      • 只能用于單邊掃描演算法,
      • copy函式的第三個引數就是輸出迭代器,ostream_iterator型別也是輸出迭代器,
    • 前向迭代器:可讀寫;多遍掃描,只能遞增(++),支持所有輸入、輸出迭代器的操作,
    • 雙向迭代器:可讀寫;多遍掃描,可遞增遞減(++、--),支持所有前向迭代器操作,
    • 隨機訪問迭代器:可讀寫,多遍掃描,支持全部迭代器運算,除了上述迭代器類別支持的操作外,還有比較兩個迭代器相對位置的關系運算子(<、<=、>、>=)、迭代器和一個整數值的加減運算(+、+=、-、-=)令迭代器在序列中前進或后退給定整數個元素、兩個迭代器上的減法運算子(-)得到其距離以及下標運算子(iter[n]與*(iter+n)等價),
      • 提供在常量時間內訪問序列中任意元素的能力,
  9. 除了輸出迭代器之外,一個高層類別的迭代器支持底層類別迭代器的所有操作,C++標準指明了泛型和數值演算法的每個得到其引數的最小迭代器類別,對每個迭代器引數來說,其能力必須與規定的最小類別至少相當,向演算法傳遞一個能力更差的迭代器會產生錯誤,

  10. 演算法形參模式:

    • alg(beg, end, other args);
    • alg(beg, end, dest, other args);
    • alg(beg, end, beg2, other args);
    • alg(beg, end, beg2, end2, other args);

    beg和end表示演算法所操作的輸入范圍

    dest引數是一個表示演算法可以寫入的目的位置的迭代器

    接受單獨的beg2或是接受beg2和end2的演算法用這些迭代器表示第二個輸入范圍

    other args表示一些演算法還接受額外的、非迭代器的特定引數

  11. 演算法命名規范:

    • 一些演算法使用多載形式傳遞一個謂詞

      unique(beg, end); // 使用==運算子比較元素
      unique(beg, end, comp); // 使用comp比較元素
      
    • _if版本的演算法

      find(beg, end, val); // 查找輸入范圍中val第一次出現的位置
      find_if(beg, end, pred); // 查找第一個令pred為真的元素
      
    • _copy區分拷貝元素的版本和不拷貝的版本

      reverse(beg, end); // 反轉輸入范圍中元素的順序
      reverse_copy(beg, end, dest); // 將元素按逆序拷貝到dest
      
    • 一些演算法同時提供_copy和_if版本

      // 從v1中洗掉奇數元素
      remove_if(v1.begin(), v1.end(), 
      			[](int i){ return i % 2; });
      // 將偶數元素從v1拷貝到v2;v1不變
      remove_copy_if(v1.begin(), v1.end(), back_inserter(v2), 
      				[](int i){ return i % 2; });
      
  12. 鏈表版本的演算法性能比對應的通用版本好得多,對于list和forward_list,應該優先使用成員函式版本的演算法而不是通用演算法,

list和forward_list成員函式版本的演算法 解釋
—— 這些操作都回傳void
lst.merge(lst2)或lst.merge(lst2, comp) 將來自lst2的元素合并入lst,lst和lst2都必須是有序的,元素將從lst2中洗掉,在合并之后,lst2變為空,第一個版本使用<運算子;第二個版本使用給定的比較操作
lst.remove(val)或lst.remove_if(pred) 呼叫erase洗掉掉與給定值相等(==)或令一元謂詞為真的每個元素
lst.reverse() 反轉lst中元素的順序
lst.sort()或lst.sort(comp) 使用<或給定比較操作順序排序元素
lst.unique()或lst.unique(pred) 呼叫erase洗掉同一個值的連續拷貝,第一個版本使用==;第二個版本使用給定的二元謂詞
list和forward_list的splice成員函式的引數 解釋
—— lst.splice(args)或flst.splice_after(args)
(p, lst2) p是一個指向lst中元素的迭代器,或一個指向flst首前位置的迭代器,函式將lst2的所有元素移動到lst中p之前的位置或者是flst中p之后的位置,將元素從lst2中洗掉,lst2的型別必須與lst或flst相同,且不能是同一個鏈表
(p, lst2, p2) p2是一個指向lst2中位值的有效的迭代器,將p2指向的元素移動到lst中,或將p2之后的元素移動到flst中,lst2可以是與lst或flst相同的鏈表
(p, lst2, b, e) b和e必須表示lst2中的合法范圍,將給定范圍中的元素從lst2移動到lst或flst,lst2與lst(或flst)可以是相同的鏈表,但p不能指向給定范圍中元素,
  1. 鏈表特有版本與通用版本間的一個至關重要的區別是鏈表版本會改變底層的容器

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/255042.html

標籤:C++

上一篇:redis哨兵宕機重啟之后。。

下一篇:Go map定義的幾種方式以及修改技巧

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more