1. 程式人生 > >讀書筆記___C++沉思錄(第17節)

讀書筆記___C++沉思錄(第17節)

泛型演算法就是一種對其所作用的資料結構做盡可能少的約束的方式表達的演算法 

以一個簡單的查詢整數陣列中的第一個等於某個值的元素為例

/*
在這個例子中,我們做了四個約束:
 分別是查詢的型別是int值
 我們對int陣列進行查詢
 我們預先已經知道了元素的書目
 我們已經預先知道了第一個元素的地址
這些約束條件降低了程式的通用性 為了提高程式的通用性 必須想辦法去掉這些約束條件
*/
const int* find1(const int *array ,int n,int x)
{
    const int *p =array;
    for(int i = 0;i<n;i++)
    {
        if(*p==x)
        return p;
        ++p;
    }
    return 0;
}

 (1)去掉查詢資料型別限制條件

/*
   最容易想到的辦法就是用模板來代替原來有的int型別的資料 重點做以下說明
 首先 const int* 替換成了 T*  這樣 T*可以完全替代const int *的功能 
 其次 int x 替換成了const T &x 這是因為很多時候 變數x是不可以複製 或者複製的代價太大的情況
 處理這些情況 一方面可以避免錯誤 另一個方面也可以提高通用性和效率
*/
template<typename T>
T* find2(T* array ,int n ,const T & x)
{
    T *p = array;
    for(int i =0;i <n;i++)
    {
        if(*p==x)
            return p;
        ++p;
    }    
    return 0;
}

 (2)去掉必須知道元素個數的限制

/*
這個位置的問題是我們使用了兩個指標 一個是元素起始位置 另一個是元素結束位置的下一個位置的指標 為什麼這樣做呢,而不是指向最後一個元素呢 因為指向最後一個元素是非常危險的事情 最後一個元素可能並不存在,根本就沒有任何元素 這種情況下,指標會指向第一個元素之前,這種情況可能導致錯誤
*/
template<typename T>
T* find3(T* array ,T* beyond, const T& x)
{

T* p = array;
while(p != beyond)
{
    if(*p==x)
    {
        return p;
    }
    ++p;
}
return 0;
}

 (3)去掉返回值的限制 使得函式的實現更簡單

/*
這段程式碼實現了指定元素的查詢 查詢到了就會返回當前元素的指標 否則就會返回為空,這樣就取消掉了元素型別的限制 使程式碼變得非常簡潔
*/
template<typename T>
T* find4(T* array ,T*beyond ,const T &x)
{
    T* p =array;
    while(p != beyond&& *p !=x)
    {

        ++p;
    }
    return p;
}

 進行上述操作後我們發現 我們依舊依賴於對指標的操作 

//泛型演算法是一種對它所作用的資料結構進行儘可能少的做約束的方式表達的演算法
/*
指標在finf4的作用有如下幾個 可以把指標作為引數 並把他們作為結果返回 也可以
比較指標想不想等 可以解除引用指標 以便得到指標所指向的變數的值 可以遞增指標
以實現指標的移動 加入我們用 P 來代替T* 就可以取消對指標的依賴

*/
template<typename P,typename T> 
P find5(P start, P beyond,const T& x)
{
        while(start !=beyond&&*start !=x)
        ++start;
        return  start;
}

 

/*
經過上述的處理 ,fund5已經能夠丟掉前文所述的四個約束條件,也不要求p是指標型別
進一步的我們可以把程式修改成更加通用的表達
*/
template<typename S, typename T>
S find5(S a,S b,const T &x)
{
    S s =a;
    while(s !=b&& *s !=x)
    {
        ++s;
    }
    return s;
}
/*
測試程式
*/
int main(int argc, char const *argv[])
{

    cout<<"測試程式"<<endl;
   int x[100];
   vector<int> v;
   for(int i =0;i<100;i++)
        {
            x[i]= i+1;
            v.push_back(i+1);
        }
   
    cout<<*find5(v.begin(),v.end(),98);
    cout<<"************"<<endl;
    system("pause");
    return 0;
}
/*
[Running] cd "d:\MyWorkSpace\VsCode\Test\" && g++ test.cpp -o test && "d:\MyWorkSpace\VsCode\Test\"test
測試程式
98************
*/

下面考慮對非陣列的查詢 

/*
有一個包含String元素的連結串列 要如何利用上述已經定義的函式來
查詢單項鍊表中的某個元素是否存在 
*/
struct Node 
{
string value;
Node *next;
};
Node *listfind(Node *p ,const string &x)
{

    while(p&&*p!= x)
        p = p->next;
   return p;
}
/*

要實現連結串列的操作 我們必須要定義一個輔助的類 用以進行幾個基本操作 *運算 !=運算 ++運算 等等
輔助類的建立如下
*/
class Nodep 
{
public:
 Nodep(Node *p): pt(p){}
 string & operator*()
 {

     return pt->value;
 }
 void operator++()
 {
    pt = pt->next;
 }
friend int operator!=(const Nodep& p, const Nodep &q)//類外面要使用這個函式
 {
    return p.pt != q.pt;
 }
 operator Node*()//轉換運算子,Nodep轉換到Node*型別
 {
     return pt;
 }
private:
 Node *pt;
};

輔助類完成之後 就可以做測試了

#include<iostream>
#include"test.h"
#include<vector>
#include<string>
#include "typedef.h"
using namespace std;
//介面模板的實現 
int main(int argc, char const *argv[])
{

    cout<<"測試程式"<<endl;
   Node n1;
   Node n2;
   Node n3;
   n1.next = &n2;
   n1.value ="100";
   n2.next =&n3;
   n2.value ="101";
   n3.next =NULL;
   n3.value="102";
   Node *p =&n1;
  
    cout<< *find5(Nodep(p),Nodep(0),"100");
    cout<<"************"<<endl;
    system("pause");
    return 0;
}
/*

[Running] cd "d:\MyWorkSpace\VsCode\Test\" && g++ test.cpp -o test && "d:\MyWorkSpace\VsCode\Test\"test
測試程式
100************
*/