日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ Primer 第十六章 模板与范型编程

發布時間:2024/6/14 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ Primer 第十六章 模板与范型编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

16.1 模板定義
??? 模板和c#范型一樣,建立一個通用的類或函數,其參數類型和返回類型不具體指定,用一個虛擬的類型來代表,通過模板化函數或類實現代碼在的重用。
??? 定義語法是:
 ? ?template<typename 類型參數>?
  返回類型 函數名(模板形參表)?
  {
    函數體?
  }
?
  或?:
  template<class 類型參數>?
  返回類型 函數名(模板形參表)?
  {?
    函數體
  }

?

??? template是一個聲明模板的關鍵字,類型參數一般用T這樣的標識符來代表一個虛擬的類型,當使用函數模板時,會將類型參數具體化。typename和class關鍵字作用都是用來表示它們之后的參數是一個類型的參數。只不過class是早期C++版本中所使用的,后來為了不與類產生混淆,所以增加個關鍵字typename。
??? 函數模板:

template?<typename?T>?//加法函數模板?
T?Add(T?x,T?y)?
{
????return?x+y;?
};?
?
int?main()?
{
????int?x=10,y=10;
????std::cout<<Add(x,y)<<std::endl;//相當于調用函數int?Add(int,int)
?
????double?x1=10.10,y1=10.10;
????std::cout<<Add(x1,y1)<<std::endl;//相當于調用函數double?Add(double,double)


?
????long?x2=9999,y2=9999;
????std::cout<<Add(x2,y2)<<std::endl;//相當于調用函數long?Add(long,long)

}

?

??? template內可以定義多個類型形參,每個形參用,分割并且所有類型前面都要用typename修飾。
??? template <typename T,typename Y> T Add(T x,Y y) ; // ok
??? template <typename T,Y> T Add(T x,Y y) ; // 錯誤,Y之前缺少修飾符

?

??? 函數模板也可以聲明inline 語法是 template <typename T,typename Y> inline T Add(T x,Y y) ;

??? 類模板:

template?<typename?T,typename?Y>
class?base
{
????public:
????????base(T?a);
????????Y?Get();

????private:
?????????T?s1
?????????T?s2?
};

int?main()?
{?
???base<int,string>?it(1,"name");?//??類后面的類型參數不能缺省

}

?

??? 和函數模板不一樣,類模板無法使用類型推斷,所以定義對象時一定要顯示傳遞類型參數。

??? 類型形參名稱有自己的作用域:

typedef?stirng?T;?//?該T與下面的類型形參不會產生沖突,不過最好不要重名以免混淆
template?<typename?T>?
?
T?Add(T?x,T?y)?
{
????typedef?stirng?T;?//
?錯誤,內部定義會產生名字沖突
????
//...?

};

?

??? 可以像申明一般函數或類一樣聲明(而不定義)。但類型形參不能省略 template <typename T,typename Y> class base ; 聲明了一個類模板。

??? 模板類型參數可以用typename 或者class 來修飾,大部分情況下二者可以互換。但有一種特殊用方法時需要typename

class?base
{
????public:
????????class?inbase{};?//?內部類

};

template?<typename?T>?
void?test()
{
????typename?T::inbase?p;?//?這時候必須要在前面加上typename,表示要定義一個類型為T類(T是類型參數)內部定義的inbase類對象

????T::inbase?p;?//?如果不加編譯會報錯,因為編譯器認為T::inbase表示T類的靜態成員inbase,所以這樣書寫語法是錯誤的
}

?

??? 要注意,這種用法需要滿足條件:類型形參T必須要定義內部類inbase 否則會編譯錯誤。

??? 模板編程中還可以在類型形參列表中定義非類型形參,這時非類型形參會被當成常量

template?<typename?T,int?i>?
?
T?Add(T?x)?//?Add(T?x,int?i)?這樣定義編譯錯誤,i?和非形參i名稱沖突

{
????return?x?+?i;
};
??
int?main()?
{?
???Add<int,10>(5);
}

?

??? 范型編程有兩個重要原則:形參盡量使用const引用(防止拷貝),形參本身操作盡量少(傳遞一個不支持函數形參體操作的類型會報錯)


16.2 實例化
??? 函數模板可以定義函數指針并予以賦值

template?<typename?T,typename?Y>?T?Get(T?x,Y?y)?;?//?聲明函數
int(*pr)?(int,string)?=?Get?;?//?定義函數指針并賦值
pr(5,"str")?;??//?用函數指針調用函數無需解引,或者(*pr)(5,"str")?;

函數模板指針作為形參時需注意重載情況。對二義性的調用要指定類型來消除
template?<typename?T>?T?Get(T?x)?;?//?聲明函數
void?fun(int?(*)?(int));
void?fun(string?(*)?(string));

fun(Get);?//?錯誤,有二義性,類型推斷后重載的兩個fun函數都能通過。

fun(Get<int>);?//?指定類型,消除了二義性


16.3 模板編譯模型

??? [1] 當編譯器看到模板定義的時候,它不立即產生代碼。 只有在看到用到模板時 ,如調用了函數模板或定義了類模板的對象的時候,編譯器才產生特定類型的模板實例 。
????[2] 一般而言,當調用函數的時候,編譯器只需要看到函數的聲明。類似地,定義類類型的對象時,類定義必須可用,但成員函數的定義不是必須存在的。因此,應該將類定義和函數聲明放在頭文件中,而普通函數和類成員函數的定義放在源文件中。
??? [3] 模板則不同:要進行實例化,編譯器必須能夠訪問定義模板的源代碼。 當調用函數模板或類模板的成員函數的時候,編譯器需要函數定義,需要哪些通常放在源文件中的代碼。
?? ?[4] 標準C++為編譯模板代碼定義了兩種模型。 所有編譯器都支持第一種模型,稱為“包含”模型( inclusion compilation model) ;只有一些編譯器支持第二種模型,“分別編譯”模型( separate compilation model) 。
?? ?[5] 在兩種模型中,構造程序的方式很大程度上是相同的:類定義和函數聲明放在頭文件中,而函數定義和成員定義放在源文件中。兩種模型的不同在于,編譯器怎樣使用來自源文件的定義 。
??? [6] 在包含編譯模型,編譯器必須看到用到的所有模板的定義。一般而言,可以通過在聲明函數模板或類模板的頭文件中添加一條#include指示使定義可用,該#include引入了包含相關定義的源文件 。
??? [7] 在分別編譯模型中,編譯器會為我們跟蹤相關的模板定義。但是,我們必須讓編譯器知道要記住給定的模板定義,可以使用export關鍵字來做這件事 。export關鍵字能夠指明給定的定義可能會需要在其他文件中產生實例化 。
??? [8] 在一個程序中,一個模板只能定義為導出一次。 一般我們在函數模板的定義中指明函數模板為導出的 ,這是通過在關鍵字template之前包含export關鍵字而實現的。對類模板使用export更復雜一些 ,記得應該在類的實現文件中使用export,否者如果在頭文件中使用了export,則該頭文件只能被程序中的一個源文件使用。
?? ?[9] 導出類的成員將自動聲明為導出的。也可以將類模板的個別成員聲明為導出的,在這種情況下,關鍵字export不在類模板本身指定,而是只在被導出的特定成員定義上指定。任意非導出成員的定義必須像在包含模型中一樣對待:定義應放在定義類模板的頭文件中。

16.4 類模板成員
??? 普通類不但定義非模板函數成員,也能定義模板函數成員:

class?base
{
????public:
????????template<typename?T>?T?Get(T?a);?//?模板函數成員申明

};

template<typename?T>?T?base::Get(T?a)?//成員函數類外部定義

{
????return?a;
}

??? 可這樣調用:
??? base obj ;
??? obj.Get<int>(20) ;
??? obj.Get("str") ;? // 類型推斷,等價于obj.Get<string>("str") ;


??? 如果是模板類

template<typename?T>
class?base
{
????public:
????????template<typename?Y>?Y?Get(Y?a);?//?模板函數成員申明

};

template<typename?T>?//?這一步不可少,確定T也是個模板類型參數

template<typename?Y>?Y?base<T>::Get(Y?a)
{
????return?a;
}

??? 可這樣調用:
??? base<string> obj ;
??? obj.Get<int>(20) ;
??? obj.Get("str") ; // 類型推斷,等價于obj.Get<string>("str") ;

?

??? 類模板或函數模板可以作為其他類的友元,不過由于其特殊性可以做一些限制。

template<typename?T>
class?he
{
????//?...

}

template<typename?T>
class?base
{
????template<typename?Y>?friend?class?he;?//?表示所有類型的模板類對象都是友元

????friend?class?he<int>;?//?表示只有int類型形參的模板類對象才是友元?
????friend?class?he<T>;???//?表示只有類型形參和base類型參數一致的模板類對象才是友元
}

?

??? 友元函數和模板類情況相似。 第一種友元可以看做是完全申明,第二種和第三種友元則需要至少在base定以前有完全申明,否則會編譯錯誤。

?

16.5 一個范型句柄類
??? 如果對上一章句柄類有充分理解范型句柄類應該非常容易掌握。

?

16.6 模板特化
??? 模板的特化(template specialization)分為兩類:函數模板的特化和類模板的特化。
??? 函數模板的特化:當函數模板需要對某些類型進行特別處理,稱為函數模板的特化。例如:

bool?IsEqual(T?t1,?T?t2)?
{
?????return?t1?==?t2;?
};

int?main()
{
?????char?str1[]?=?"Hello";

?????char?str2[]?=?"Hello";

?????cout?<<?IsEqual(1,?1)?<<?endl;

?????cout?<<?IsEqual(str1,?str2)?<<?endl;???//輸出0

?return?0;
?
}

?

??? 最后一行比較字符串是否相等。由于對于傳入的參數是char *類型的,IsEqual函數模板只是簡單的比較了傳入參數的值,即兩個指針是否相等,因此這里打印0。顯然,這與我們的初衷不符。因此,sEqual函數模板需要對char *類型進行特別處理,即特化:

template?<>?bool?IsEqual(char*?t1,?char*?t2)?// 函數模板特化
{
????return?strcmp(t1,?t2)?==?0;
}


??? 這樣,當IsEqual函數的參數類型為char* 時,就會調用IsEqual特化的版本,而不會再由函數模板實例化。

??? 類模板的特化:與函數模板類似,當類模板內需要對某些類型進行特別處理時,使用類模板的特化。例如:

template?<class?T>
class?compare?
{
??public:
????bool?IsEqual(T?t1,?T?t2)
????{
???????return?t1?==?t2;
????}
};???
?
int?main()?
{
??char?str1[]?=?"Hello";

??char?str2[]?=?"Hello";

??compare<int>?c1;

??compare<char?*>?c2;
??cout?<<?c1.IsEqual(1,?1)?<<?endl;?//比較兩個int類型的參數


??cout?<<?c2.IsEqual(str1,?str2)?<<?endl;???//比較兩個char?*類型的參數
??return?0;?
}

?

??? 這里最后一行也是調用模板類compare<char*>的IsEqual進行兩個字符串比較,顯然這里存在的問題和上面函數模板中的一樣,我們需要比較兩個字符串的內容,而

不是僅僅比較兩個字符指針。因此,需要使用類模板的特化:

template<>class?compare<char?*>?//特化(char*)?
{
??public:
?????bool?IsEqual(char*?t1,?char*?t2)
?????{?
????????return?strcmp(t1,?t2)?==?0;??//使用strcmp比較字符串

?????}

};

?

??? 注意:進行類模板的特化時,需要特化所有的成員變量及成員函數。

轉載于:https://www.cnblogs.com/kingcat/archive/2012/05/23/2514939.html

總結

以上是生活随笔為你收集整理的C++ Primer 第十六章 模板与范型编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。