STL and Design Pattern

光風霽月發表於2024-07-31

Design Patterns[TODO]

0x01. 依賴轉置原則

ref1

例如人吃巧克力:

public interface IChocolates{}

public class Oreo implements IChocolates {}

public class Dove implements IChocolates {}

public interface Person { void eat( IChocolates chocolates ); }

上面的例子中人對巧克力產生了依賴,那人吃的行為依賴其實跟巧克力沒有關係,在巧克力出現之前就已經存在了,因此吃的動作依賴的介面應該是人本身內部的概念,這個介面的歸屬權應該屬於人,概念應該為可食用的(edible)。因此人對巧克力的依賴關係應該倒置為巧克力對可食用介面的依賴。這樣倒置之後對人來說具有了更好的擴充套件性,不僅可以吃各種不同的巧克力,還可以吃餅乾,米飯,魚肉等等其它任何可吃的東西。

public interface IChocolates extends IEdible{}

public class Oreo implements IChocolates {}

public class Dove implements IChocolates {}

public interface Person { void eat( IEdible edible ); }

ref2

REF

ref3

REF

在例子中,如果我們讓上層 dirver 依賴於底層的細節 car,那麼就完全錯誤了。

我們應該讓 diver 依賴於抽象的 CAR,讓具體的 car 繼承自抽象的 CAR。

0x02 裝飾模式

ref1

原文連結

1.什麼是裝飾模式

裝飾模式是一種用於替代繼承的技術。它無需定義子類卻可以給物件動態增加職責,使用物件之間的關聯關係取代類之間的繼承關係。

舉個例子:一張照片,不改變照片本身,給它增加一個相框,使他增加防潮的功能,這就是裝飾模式。

裝飾模式是一種物件結構型模式,他以對客戶透明的方式動態的給一個物件附加上更多的責任,可以在不需要建立更多子類的情況下讓物件的功能得以擴充套件。

2.裝飾模式的結構

(1)Component(抽象構件):它是具體構件和抽象裝飾類的共同父類,宣告瞭在具體構建中實現的業務方法,它的引入可以使客戶端以一致的方式處理未被裝飾的物件以及裝飾之後的物件,實現客戶端的透明操作。

(2)ConcreteComponent(具體構件):它是抽象構件的子類,用於定義具體的構建物件,實現了在抽象構建中宣告的方法,裝飾類可以給它增加額外的職責。

(3)Decorator(抽象裝飾類):他也是抽象構件的子類,用於給具體構件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象構件物件的引用,透過該引用可以呼叫裝飾之前構件物件的方法,並透過其子類擴充套件該方法,以達到裝飾的目的。

(4)ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向構件新增新的職責。每一個具體裝飾類都定義了一些新的行為,他可以呼叫在抽象裝飾類中定義的方法,並可以增加新的方法用於擴充物件的行為。

ref2

refreence

####### 更好的 ref – c++ 例項

0x03 代理模式

引用計數

引用計數

jk時間 – 好文

0x04 工廠模式

現實生活中,工廠是負責生產產品的;同樣在設計模式中,簡單工廠模式我們可以理解為負責生產物件的一個類,稱為“工廠類”。

0X05 原型模式

原型模式定義了一個 virtual 複製建構函式,C++ 有嗎? C++ 沒有虛複製構造

ref1

ref2

ref3

STL[TODO]

prelogue

0x01 導讀

候捷 STL 與 範型程式設計學習筆記。

0x02 英語

identity:同一,本身

0x03 problem

1

2

3

4

一、container

大家風範:用前置++,–實現後置++,–

仿函式:函式物件

關聯:有key和value

array就是把陣列包裝成一個class

deque:dai ke

set, map:紅黑樹.c++並未規定必須使用紅黑樹,只不過紅黑樹效率高,因此各大制定標準庫的公司都使用紅黑樹。

set的key就是value,value就是key。

雜湊表由一個個桶(bucket)組成,每個桶都是一個連結串列(link)。現在雜湊表大多采用分離連結串列發(yxc:拉鍊法)實現。

標頭檔案有保護機制,多次引入沒問題

vector空間兩倍增長(成長)

(2)vector 有一個機制是這樣的,如果新加入一個元素,比如透過push_back(),但是size 大於了capacity,那麼vector 就會重新找一塊更大的地方再把資料放進去。重新分配的過程:申請一塊新的記憶體 > 複製資料 > 釋放原記憶體

2、vector擴容怎麼複製?

經常問的一個問題,vector怎麼擴容?最簡單的回答就是先申請記憶體,再複製,最後銷燬原來的記憶體,然後是1.5倍還是2倍,解釋一下。

但是!我看到有人問,怎麼複製???what???

沒想到吧,看書還是看漏了。

複製用的是uninitialized_copy,如果複製的是POD(標量型別,也就是trivial)呼叫的是copy(自己去看STL 的copy實現),如果是non-POD使用for迴圈遍歷,呼叫construct,一個一個的構造,針對char和wchar_t,uninitialized_copy直接用memmove來執行復制行為,更加快。

版權宣告:本文為CSDN博主「m0_60126088」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/m0_60126088/article/details/119616254

雖然標準庫提供了sort函式,但是一些函式也提供了自己的sort,當容器存在自己的sort的時候,優先使用容器自己的,它自己的肯定更好,不然它提供這個函式幹嘛。

forward_list 和 list 提供了自己的 sort

forward_list使用頭插法。因此只能在連結串列頭插入元素。

deque容器底層實現

queue 和 stack 由於元素進出的順序是死的(固定的),因此他們沒有迭代器,如果有迭代器的話,我們就有可能透過迭代器改變元素進出的順序,這是不合理的。

set,map底層是紅黑樹(自平衡二叉查詢樹)

multimap不可以用下標做 iterator

hashtable 的 bucket 比 元素個數還有要多是合理的,因為元素中間會空出很多 bucket。事實上,bucket的個數肯定比元素個數多,否則的話 bucket 就要重新擴充變為原來的(大約)兩倍。(經驗法則,避免 bucket 的長度太長)(以空間換時間)

map的底部就是一個 pair

map,set 會丟掉重複元素

從測試結果我們可以看出,使用insert()插入元素的方式並不能覆蓋掉相同key的值;而使用[]方式則可以覆蓋掉之前的值。為什麼會出現這樣的結果呢?

原因分析
我們可以透過原始碼來找原因,在map的原始碼中,insert方法是這樣定義的:

pair<iterator,bool> insert(const value_type& __x) 
    { return _M_t.insert_unique(__x); }

他呼叫_M_t.insert_unique(_x)方法,該方法會首先遍歷整個集合,判斷是否存在相同的key,如果存在則直接返回,放棄插入操作。如果不存在才進行插入。
而[]方式是透過過載[]運算子來實現的,它直接進行插入或覆蓋

今天除錯程式,弄得有點糾結。無意間,和一幫同事討論起一個問題:程序結束作業系統會回收new的記憶體嗎?在自己的印象中,一直固執地認為,在使用C++操作分配物件記憶體後,如果程式設計師自己不用相應的delete操作回收的話,這塊從堆記憶體是一直存在。在討論中,有同事提醒說,在程序結束後,new操作的記憶體會被回收。但也只是結論,也說不出具體理由。

沒關係,何不google一下,一查下去,答案已是清晰:

“記憶體洩漏不是系統無法回收那片記憶體,而是你自己的應用程式無法使用那片記憶體。當你程式結束時,你所有分配的記憶體自動都被系統回收,不存在洩漏問題。但是在你程式的生命期內,如果你分配的記憶體都不回收,你將很快沒記憶體使用。”再用自己的一句話來概括的話就是:作業系統本身就有記憶體管理的職責,一般而言,用malloc、new操作分配的記憶體,在程序結束後,作業系統是會自己的回收的。但某些系統態的資源,用特殊的系統API申請的記憶體就不一定了,比如:linux中的shmget申請的共享記憶體,就與程序結束無關了。

經過這一番查詢,不禁自己為自己汗顏了一把,之前還以為自己對記憶體管理理解較深,現在才明白:在沒有認真深入地鑽研過作業系統核心原理之前,所有對記憶體管理的理解還是浮在表面上的。這次討論也分外地提醒自己,自己知識的盲區還有許多,還要不斷的踏踏實實地努力學習啊!


版權宣告:本文為CSDN博主「stanjiang2010」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/stanjiang2010/article/details/5386647

不建議使用分配器。

oop企圖將data和method關聯在一起

gp企圖將data和method分離開

容器記憶體分配

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>

using namespace std;

void test1();
void test2();
void test3();
void test4();

class test 
{
    public:
        typedef pair<int,int> PII;
        PII p;
};

template<typename T>
class complex
{
    public:
        complex(T _val) : val(_val) {}
        T val;
        template<typename U>
        friend complex<U> cal(complex<U> a, complex<U> b);
};

template<typename T>
complex<T> cal(complex<T> a, complex <T> b)
{
    complex<T> c(a.val + b.val);
    return c;
    // return complex(a.val + b.val);
}


int main()
{
    test3();
    return 0;
}

/*====================================================*/

void test3()
{
    test::PII p = {1,2};
    cout << p.first << ' ' << p.second << endl;
    complex<int> a(1);
    complex<int> b(2);
    cout << a.val << ' ' << b.val << endl;
    complex<int> c = cal(a, b);
    cout << c.val << endl;
}

void test1()
{
    vector<int> vec;
    cout << vec.capacity() << endl;
    cout << &vec << endl;
    vec.push_back(1);   // add -> cap:1
    cout << vec.capacity() << endl;
    cout << &vec << endl;
    vec.push_back(1);   // add -> cap:2
    cout << vec.capacity() << endl;
    cout << &(vec[1]) << endl;
    vec.push_back(1);   // add -> cap:4
    cout << vec.capacity() << endl;
    cout << &(vec[1]) << endl;
    vec.push_back(1);
    vec.push_back(1);   // add -> cap:8
    cout << vec.capacity() << endl;
    cout << &(vec[1]) << endl;
    for(int i = 0; i < 1000000; i ++ )   vec.push_back(i);
    cout << &vec << endl;
}

void test2()
{
    map<int,int> m;
    m.insert({1,1});
    cout << m[1] << endl;
    m.insert({1,2});
    cout << m[1] << endl;
    cout << m.size() << endl;
    m[2] = 3;
    cout << m.size() << endl;
    m[1] = 3;
    cout << m[1] << endl;
}

二、alloactor

容器的幕後英雄。

所有的分配和釋放動作最後都會跑到 malloc 和 free 去。

分配器的 allocate 和 deallocate 最終都是使用 malloc 和 free

直接使用分配器在釋放記憶體的時候,還需要指出需要釋放的記憶體大小。

因此並不建議直接使用分配器分配記憶體。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

void* operator new(size_t size, string s)
{
    cout << "s: " << s << endl;
    return malloc(size);
}

int main()
{
    int *x = new("hello") int;
    auto a = allocator<int>();   
    int *p = a.allocate(512, 0);
    a.deallocate(p, 512);
    return 0;
}

三、list

不允許 x++ ++

允許 ++ ++ x

所有的容器區間都是前閉後開

在連結串列中為了保證這個要求,必須要設定一個虛節點(為空)

四、vector

記憶體的擴充一般不會是原地擴充,它會在另一個地方找記憶體。

vector<int> vec;
cout << sizeof(vec) << endl; // 24 = 3 * 8(point)

public 繼承表示一種 is-a 的關係

五、array

array 內部的資料就是一個真的陣列,它沒有tors

六、deque

號稱自己擁有連續空間^^

連續是假象,分段是事實。

deque(dei ke)其實是分段連續的。deque的資料由許多個段組成,段與段之間不連續,每個段內連續。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    deque<int> q;
    for(int i = 0; i < 10000; i ++ )    q.push_back(i);
    for(int i = 0; i < 10000 - 1; i ++ )
    {
        int *x = &q[i + 1], *y = &q[i];
        if(y - x != -1)  
            cout << y - x << ' ' << &q[i] << ' ' << &q[i + 1] << endl;
    }
    return 0;
}
// 測試結果
-5 0x19d4e6c 0x19d4e80
-5 0x19d507c 0x19d5090
-5 0x19d528c 0x19d52a0
-5 0x19d549c 0x19d54b0
-45 0x19d56ac 0x19d5760

deque 的底部實現是一個 vector(稱為 map ?? ),vector 的每個元素是一個指標,每個指標指向一個緩衝區 buffer。

其迭代器是一個類

  1. first:當前所在 buffer 的起點
  2. last:當前所在 buffer 的重點
  3. node:處於(deque控制中心) 的那個段
  4. cur:處於當前 buffer 的位置

first 和 last 相當於兩個哨兵[first,last) 前閉後開

迭代器在移動的過程中,每次移動都需要判斷是否移動到了當前段的末尾,來決定是否需要調到下一個段。(效率低!每次都需要判斷一下)

和 vector 一樣,deque 也是 2 倍增長的,因此它也有 capacity,事實上標準庫並未規定容器的增長細節,但是大多數實現都使用了 2 倍增長。

#include <bits/stdc++.h>

using namespace std;

const int N = 1000010;

int main()
{
    deque<int> q;
    vector<int> vec;
    clock_t start;
    //
    start = clock();
    for(int i = 0; i < N; i ++ )    q.push_back(i);
    cout << (clock() - start) << endl;
    //
    start = clock();
    for(int i = 0; i < N; i ++ )    vec.push_back(i);
    cout << (clock() - start) << endl;
    //
    start = clock();
    int sum = 0;
    for(int i = 0; i < N; i ++ )    sum = q[i];
    cout << (clock() - start) << endl;
    //
    start = clock();
    for(int i = 0; i < N; i ++ )    sum = vec[i];
    cout << (clock() - start) << endl;
    //
    return 0;
}
15584
16521
79156
3326

deque 在擴充的時候 copy 方式是 copy 到新空間的中間位置,以方便前面和後面的擴充。

八、queue

queue<int, vector<int>> q; 可以透過編譯

但是當我們使用 q.pop(); 編譯器報錯

這說明編譯器對模板不會進行一個全面的、完整的檢測

只有當你用到時才會進行檢測

九、stack

十、Red-Black tree

關聯式容器甚至可以看做一個小型資料庫

關聯式容器的兩個重要的底層資料結構:紅黑樹和雜湊表

紅黑樹就是一顆高度平衡的二叉搜尋樹

紅黑樹提供遍歷操作和迭代器

編譯器會將 size = 0 的class 的size 設為 1

同 link,rb_tree 也設定了一個空節點實現前閉後開區間。

紅黑樹有【元素自動排序】特性

value[key,date]

#include <bits/stdc++.h>

using namespace std;

void test1()
{
    set<int, greater<int>> s; 
    s.insert(1);
    s.insert(2);
    s.insert(3);
    for(auto &x : s)    cout << x << endl;
    set<int>::value_type x = 23.2332;
    set<int>::key_type y = 2.3232;
    cout << x << ' ' << y << endl; 
}
 
 void test2()
 {
    map<int,int>::value_type p = {1,2};
    map<int,int>::value_type::first_type px = 1;
    map<int,int>::value_type::second_type py = 2;
    map<char,char>::key_type c1 = 65;
    map<char,char>::mapped_type c2 = 97;
    
    cout << p.first << ' ' << p.second << endl;
    cout << px << ' ' << py << endl;
    cout << c1 << ' ' << c2 << endl;
 }
 
int main()
{  
    test1();
    test2();
    return 0;
}

十一、hashtable

hashtable 中有很多經驗值

經驗法則:如果某個 bucket 的元素個數比 bucket 的個數還要多,就要兩倍擴充 bucket 並重新放入元素(rehashing)。

十二、 STL 六大部件

容器替我們處理了記憶體分配上的問題,我們只管往容器裡面新增刪除元素,至於資料在記憶體上是怎樣分配的,容器替我們解決了。

容器本身並不能處理記憶體,它是透過分配器來實現記憶體的分配與回收的。

演算法透過迭代器操作容器。

  1. 容器
  2. 分配器
  3. 演算法
  4. 迭代器
  5. 仿函式
  6. 介面卡

十三、新新增待處理

ctor 有必要新增

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

class Base {
public:
    // Base() {
        
    // }
    Base(int val) {
        cout << "Base" << endl;
    }
    
};

class Derived : public Base {
public:
    Derived(int val) {
        cout << "Derived" << endl;
    }
};

int main()
{
    Derived a(1);   // wrong
    return 0;
}

Explicitly specializer:全特化


模版別名。

typedef 不可以使用引數

define 雖然可以指定引數,但是必須用()包圍起來,而我們希望使用 <> 包圍起來。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

#define v(T) vector<T>

using namespace std;

template<typename T>
using vec = vector<pair<T,T>>;

int main()
{
    v(int) a; // 很奇怪
    vec<pair<double,double>> v;
    return 0;
}

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;

template<typename Container>
void test(Container c)
{
    // 從容器中取出迭代器
    // 每個迭代器都有一個 value_type
    // 透過 value_type 就可以得到容器元素的型別
    typedef typename iterator_traits<typename Container::iterator>::value_type Valtype;
    
    for(int i = 0; i < 10; i ++ )
        c.insert(c.end(), i);
    for(auto &x : c)    cout << x << ' ';
    cout << endl;
}

template<typename T,
            template<typename U>
            typename Container
        >
class C {
private:
    Container<T> c;
public:
    C() {
        for(int i = 0; i < 10; i ++ )
        c.insert(c.end(), i);
        for(auto &x : c)    cout << x << ' ';
        cout << endl;
    }
};

int main()
{
    test(vector<int>());
    C<int, vector> c;
    // C<int, list> c;
    // C<int, queue> c;
    // C<int, map> c;
    // C<int, set> c;
    // C<int, stack> c;
    
    return 0;
}

模板模板引數

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

template<typename T>
class Foo {
public:
    T a, b;
    Foo() {
        a = 1, b = 2;
        cout << "Ctor for Foo::" << endl;
    }
    ~Foo() {
        cout << "Dtor for Foo::" << endl;
    }
};

template<typename T,
            template<typename U>
            class Container
        >
class C {
private:
    Foo<T> c;
public:
    C() {
        cout << "Ctor for C" << endl;    
    }
    void show() {
        cout << "begin\n";
        cout << c.a << ' ' << c.b << endl;
        cout << "end\n";
    }
};

template<typename T,
            template<typename U>
            class Container
        >
void f(Container<T> c) {
}
        
        

int main()
{
    // f(Foo<int>());
    C<int, Foo> c;
    c.show();
    return 0;
}

stl

traits:特徵,特性

萃取機。

你丟給他某個東西,它可以萃取出這個東西的某些特徵。

associated type:

category

Value_type

Difference_type

pointer

reference

中介軟體 for:

class iterator

Native pointer

const native pointer

演算法所需要的資訊:主要是迭代器的是怎麼移動的(方向,大小)

雖然雖然可以直接向迭代器詢問,迭代器也可以直接回答,但是如果迭代器不是一個 class,即迭代器如果是一個 native point 的話,他就沒有 associate type(5個type),也就無法回答。這時候我們需要一箇中間層(trait)來回答演算法的詢問。

iterator_traits<iterator>::type;

Bidirectional:雙向


typeid

#include <typeinfo>
typeid(type).name();

給 typeid 傳入一個 type(typeid(type)),他就會生成一個物件,這個物件有 name() 方法。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <unordered_set>
#include <typeinfo>

using namespace std;

void out_tag(random_access_iterator_tag) {
    cout << "random_access_iterator" << endl;
}

void out_tag(bidirectional_iterator_tag) {
    cout << "bidrectional_iterator_tag" << endl;
}

void out_tag(forward_iterator_tag) {
    cout << "forward_iterator_tag" << endl;
}


template<typename Itr>
void out(Itr iter) {
    typename iterator_traits<Itr>::iterator_category category_obj;
    out_tag(category_obj);
    typedef typename iterator_traits<Itr>::iterator_category category_type;
    out_tag(category_type());
    cout << "typeid: " << typeid(category_obj).name() << endl << endl;
    cout << "typeid: " << typeid(category_type()).name() << endl << endl;
}

int main()
{
    out(vector<int>::iterator());
    out(set<int>::iterator());
    out(multiset<int>::iterator());
    out(unordered_set<int>::iterator());
    
    
    return 0;
}

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

class Base {
};

class Derived : public Base{
};

void f(Base b) {
    cout << "you call f()" << endl;
}

void g(Base *b) {
    cout << "you call g(*)" << endl;
}

int main()
{
    Derived d;
    f(d);
    Base *b = new Derived();
    g(b);
    Derived *pd = new Derived();
    g(pd);
    
    return 0;
}
/* 繼承表示 is-a 關係
因此可以把子類放到父類裡面(子類 is-a 父類)*/

11 個演算法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

class Myclass {
public:
    int val;
    Myclass() { cout << "Myclass" << endl; val = 0; }
    ~Myclass() { cout << "Dtor for Myclass" << endl; }
    bool operator()(int a, int b) {
        return a < b; 
    }
};

struct Mystruct {
    bool operator()(int a, int b) {
        return a < b;
    }  
}Mstruct;

bool cmp(int a, int b) {
    return a < b;
}

int main()
{
    vector<int> a{1};
    sort(a.begin(), a.end(), Myclass()); // 生成一個臨時物件,在 main 函式結束之前便結束
    sort(a.begin(), a.end(), Mstruct);
    sort(a.begin(), a.end(), minus<int>());
    sort(a.begin(), a.end(), cmp);
    
    cout << "main-end" << endl;
    return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iterator>

using namespace std;

/* 1. accumulate 累計(不是累加,因為還可以指定其他運算方式)
** 指定運算操作時,如果我們傳入的是一個函式,那麼直接傳遞函式名即可,
    因為此時相當於我們傳入了一個函式指標,函式指標可以類似於函式直接呼叫。
    例如:void (*ptr)() = &func;
          ptr(); // <==> func();
    但如果我們傳入的是一個過載了 () 的函式物件
    我們就必須穿入一個物件,因為只有 class 被例項化為一個 object,
    才能呼叫它自己的成員函式。
    例如: struct Myclass {
                int operator(int x, int y) { return x + y; }
            }myobj;
            class Myclass { 
                int operator(int x, int y) { return x + y; }
            };
            直接傳入 myobj 或者 myclass() 即可,
    如果成員函式是一個模版,還需要指定 <type>
    例如:標準庫的 minus
            此時我們傳入 minus<int>(); // 傳入一個臨時物件
*/

/*  2. for_each()
**  例如:
        void func(int x){ cout << x << ' '; }
        vector<int> a{1, 2, 3};   
        for_each(a.begin(), a.end(), func); // 1 2 3
*/

/*  3. replace, replace_if, replace_copy
        count, count_if
    ``` c++
    vector<int> a{1, 4, 3, 4, 5};
    replace(a.begin(), a.end(), 4, 10);
    for(auto &x : a)    cout << x << ' ';   
    cout << endl;   // 1 10 3 10 5 
    replace_if(a.begin(), a.end(), bind2nd(less<int>(), 10), 0);
    for(auto &x : a)    cout << x << ' ';   
    cout << endl;   // 0 10 0 10 0 
    vector<int> b(a.size()); // 必須為 b 分配空間,copy 不會分配空間
    replace_copy(a.begin(), a.end(), b.begin(), 0, 1);
    for(auto &x : b)    cout << x << ' ';   
    cout << endl;   // 1 10 1 10 1 
    vector<int> c; // 或者使用 back_inserter
    replace_copy(a.begin(), a.end(), back_inserter(c), 0, 1);
    for(auto &x : c)    cout << x << ' ';   
    cout << endl;   // 1 10 1 10 1 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iterator>

using namespace std;

/* 1. accumulate 累計(不是累加,因為還可以指定其他運算方式)
** 指定運算操作時,如果我們傳入的是一個函式,那麼直接傳遞函式名即可,
    因為此時相當於我們傳入了一個函式指標,函式指標可以類似於函式直接呼叫。
    例如:void (*ptr)() = &func;
          ptr(); // <==> func();
    但如果我們傳入的是一個過載了 () 的函式物件
    我們就必須穿入一個物件,因為只有 class 被例項化為一個 object,
    才能呼叫它自己的成員函式。
    例如: struct Myclass {
                int operator(int x, int y) { return x + y; }
            }myobj;
            class Myclass { 
                int operator(int x, int y) { return x + y; }
            };
            直接傳入 myobj 或者 myclass() 即可,
    如果成員函式是一個模版,還需要指定 <type>
    例如:標準庫的 minus
            此時我們傳入 minus<int>(); // 傳入一個臨時物件
*/

/*  2. for_each()
**  例如:
        void func(int x){ cout << x << ' '; }
        vector<int> a{1, 2, 3};   
        for_each(a.begin(), a.end(), func); // 1 2 3
*/

/*  3. replace, replace_if, replace_copy
        count, count_if
     c++
    vector<int> a{1, 4, 3, 4, 5};
    replace(a.begin(), a.end(), 4, 10);
    for(auto &x : a)    cout << x << ' ';   
    cout << endl;   // 1 10 3 10 5 
    replace_if(a.begin(), a.end(), bind2nd(less<int>(), 10), 0);
    for(auto &x : a)    cout << x << ' ';   
    cout << endl;   // 0 10 0 10 0 
    vector<int> b(a.size()); // 必須為 b 分配空間,copy 不會分配空間
    replace_copy(a.begin(), a.end(), b.begin(), 0, 1);
    for(auto &x : b)    cout << x << ' ';   
    cout << endl;   // 1 10 1 10 1 
    vector<int> c; // 或者使用 back_inserter
    replace_copy(a.begin(), a.end(), back_inserter(c), 0, 1);
    for(auto &x : c)    cout << x << ' ';   
    cout << endl;   // 1 10 1 10 1 

    如果函式名後面跟著 _if 表示你要傳入一個“條件”
    如果函式名後面跟著 _copy 表示你要傳入一個 iterator 用來放置 copy 的元素
        並且作用結果不會作用到原迭代器身上,只是 copy 到新迭代器
*/

/*  4. find & find_if -- 線性時間複雜度 */

/*  5. sort
**   不要拿我們寫的 cmp 用於 sort list 和 forward_list
      因為他們的地址不是連續的。而我們寫的 cmp 是連續的
*/

/*  6. binary_search
**   二分查詢的前提是有序
     binary_search() 呼叫了 lower_bound()
     然後對 lower_bound 找到的位置進行比較
        first = lower_bound(first, val);
        return (first != last) && (val >= *first);
        // 我感覺後面的 val>=*first 不太必要的,因為如果你 first != last 的話
            // 就說明你肯定找到了,既然找到了,那就應該是合理的!
                // 很奇怪,不知道為啥
     關於 lower/upper_bound
     候捷的解釋:
        lower_bound 查詢的是 在不改變原資料順序的前提下
            插入元素可以放入的 最低點
        upper_bound 查詢的是 在不改變原資料順序的前提下
            插入元素可以放入的 最高點
     網路上一般的解釋為:
        lower 查詢 <=
        upper 查詢 >
     其實一樣,只不過說法不同
*/


int main()
{
    vector<int> a{1,2,2,2,3};
    auto begin = lower_bound(a.begin(), a.end(),1);
    auto end = upper_bound(a.begin(), a.end(),1);
    cout << *begin << ' ' << *end << endl;
    return 0;
}

adapter

  1. 容器介面卡
  2. 迭代器介面卡
  3. 仿函式介面卡

一個物件 A 想擁有物件 B 的功能,有兩種方式:

  1. inherit,繼承
  2. composition,複合

adapter 是複合

typedef:

binary_function

  1. first_arguement_type
  2. second_arguement_type
  3. result_type

unary_function

  1. Argument_type
  2. Result_type

typename + () 產生臨時物件

借用函式模版的實參推導來完成類别範本的實參。

如果我們直接寫類别範本的實參 <>,我們可能不太好寫出型別,我們可以新增一層中間層(函式模板),讓函式模板來完成實參型別的推導,然後將型別傳給類别範本。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

void test1() {
    vector<int> a{1,10,2,1,2,5,1,2,3};
    typename less<int>::second_argument_type x = 1 << 31;
    typename less<int>::first_argument_type y = 1 << 31;
    typename less<int>::result_type z = 1 << 31;
    cout << x << ' ' << y << ' ' << z << endl;
}

int main()
{
    auto p = bind2nd(less<int>(), 10);
    binder2nd<less<int>> b2;  // wrong,已經棄用
    return 0;
}

Argument 並無引數的意思,之所以被拿來做(引數、變數)的意思是歷史原因。


bind

取代了 bind2nd, binder2nd, bind1st, binder1st

沒寫建構函式也可以用 {} 初始化

placeholoders

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

void test1() {
    vector<int> a{1,10,2,1,2,5,1,2,3};
    typename less<int>::second_argument_type x = 1 << 31;
    typename less<int>::first_argument_type y = 1 << 31;
    typename less<int>::result_type z = 1 << 31;
    cout << x << ' ' << y << ' ' << z << endl;
}

void test2() {
    auto p = bind2nd(less<int>(), 10);
    // binder2nd<less<int>> b2;  // wrong,已經棄用
}

double my_divide(double a, double b) {
    return a / b;
}

void test3() {
    // _1, _2。。 在 std::placeholders 裡面
    // auto f1 = bind(my_divide, std::placeholders::_1, std::placeholders::_2);
    // cout << f1(10, 2) << endl;   // 5
    
    // auto f2 = bind(my_divide, std::placeholders::_2, std::placeholders::_1);
    // cout << f2(10, 2) << endl;   // 0.5
    
    // auto f3 = bind(my_divide, 10, std::placeholders::_1);   // 這裡 10 被繫結到第一個引數
    // cout << f3(200) << endl;  // 10 / 200
   
    // auto f5 = bind(my_divide, std::placeholders::_1, 10);
    // cout << f5(100) << endl;    // 10
   
   
    /*  --  */
    /* 佔位符_n會選擇 [我們傳入的] 第n個引數 */
    auto f4 = bind(my_divide, 10, std::placeholders::_2);   // 這裡 10 被繫結到第一個引數
    cout << f4(3,2) << endl;   // 3.33,選擇我們傳入的第二個引數2
   
    auto f6 = bind(my_divide, std::placeholders::_2, 10);
    cout << f6(3,5) << endl;    // 5 / 10,選擇我們傳入的第二個引數5
}

int main()
{
    test3();
    return 0;
}

相關文章