[原創文章歡迎轉載,但請保留作者信息]
Justin?
于 2010-03-04
話說看書看到第七章,大師開始講模板。
第
41
課首先要講的是:面向對象
(OO)
編程強調的是具體的接口和運行時的多態;而模板編程恰恰相反,側重于模糊的接口以及編譯期的多態。
具體的接口
(explicit interface)
有著具體的類型
(
包括參數的,內部變量的,返回值的等等
)
,這些類型被大師稱為接口的特征
(signature)
;而模糊的接口卻沒有這些限制,取而代之的是表達式
(expression)
。
運行時的多態由虛擬機制而生,對象的運行時類型決定了真正得到執行的函數
/
接口
/
方法是什么;這和編譯期的多態有些類似,不過后者多態特性是在程序編譯期間呈現的:不同的模板參數決定了不同的函數會被調用。
例子就不舉了,如果不清楚什么是模板,那么最好
Google
一下,會有一堆的入門讀物。(哦對了,不知道現在Google是否會需要翻墻……)
OK
,下一課吧。
42
課的中心思想是
typename
的兩重意義。
下面的課前作業做了沒?template?<class?T>?class?TClass1;
template?<typename?T>?class?TClass2;
TClass1
和
TClass2
有什么區別?
答案是沒有區別。因為當定義模板參數時,用
class
和
typename
是沒有差異的。
既然用了兩個不同的名字,自然會有不一樣的地方。
下面便是一處應該用
typename
而不能用
class
的情形:
由于模板的中接口的模糊性質,下面的函數模板便有了這樣那樣的問題:
見下面的程序片段:T::name
的本意可能是模板
T
所指代的類中的
name
類型,而
pName
就是指向這種類型對象的指針。
但是模板中的接口是模糊的,所以同樣的模板會因為
T
的不同而產出不一樣的實際代碼。如果
T
::
name
不是一個類型,而是
T
的一個成員,然后如果人品真的很差,
pName
是一個全局變量,這一句就變成了將
T::name
和
pName
相乘!
(
看到這里不得不佩服大師舉例子的能力,這樣都想得到
……)template?<typename?T>
void?TFunc()
{
?? T::name?*?pName;?//?this?won't?even?compile
?? //?and?it?should?be
?? //?typename?T::name?*?pName;
?? //..
}
會發生這種慘案的原因是因為
C++
在這種情形下,如果沒有顯式聲明,就默認
T::name
不是個類型。于是這個時候就需要
typename
來顯式地告訴編譯器這個其實是個類型而非其他東東。當然,在這里
class
是無法替代
typename
的作用的。
書中把上面的情況用語法來描述:
typename
必須放在前面修飾
“
嵌套從屬類型名
”(nested dependent type name)
。
個人覺得太拗口難記,所以還是記著上面的例子好了。
不過這個規定也還是有個例外,那就是
typename
作用于
“
嵌套從屬類型名
”
時不能用于
(1)
基類列表,以及
(2)
成員初始化列表中。如下所示template?<typename?T>
class?TClass():public?BClass<T>::NestedClass
//?????????????---------------------->?(1)no?typename?is?needed,?nor?allowed
{
???explicit?TClass(int?x)?:?BClass<T>::NestedClass(x)
//??????????????????????????------------------------->?(2)no?typename?is?needed,?nor?allowed
???{
??????//..
???}
???//..
};
如果覺得這種用法會打很多字
(@#
¥
%)
,
可以用
typedef
來減少一點工作量,改寫后的例子是這樣的:template?<typename?T>
void?TFunc()
{
???typedef?typename?T::name?*?pTname;
???//ok,?now?we?can?do?this:
???pTname?pname1;
???pTname?pname2;
//..
}