《深度探索C++物件模型》讀書筆記(7)。
***Template的“具現”行為***
template class中的任何member都只能通過template class的某個實體來存取或操作。
Point<float>::Status s; // ok
Point::Status s; // error
如果我們定義一個指標,指向特定的實體,像這樣:
Point<float> *ptr = 0;
由於這是一個指向class object的指標,本身並不是一個class object,編譯器不需要知道與該class有關的任何members資料。所以將“Point的一個float實體”具現也就沒有必要。
如果不是一個pointer而是reference,假設:
Point<float> &ref = 0;
這個定義的真正語意會被擴充為:
// 內部擴充
Point<float> temp(float(0));
Point<float> &ref = temp;
以上轉化是因為reference並不是無物(no object)的代名詞,0被視作整數,必須被轉換為類型Point<float>的一個對象。
然而,member functions只有在member functions被使用的時候,C++ Standard才要求它們被“具現”出來。這個規則的由來主要有兩個原因:
(1)空間和效率的考慮。對於未使用的函數進行“具現”將會花費大量的時間和空間;
(2)尚未實現的功能。並不是一個template具現出來的所有類型一定能夠完整支援一組member functions,因而只需具現真正需要的member functions.
舉個例子:
Point<float> *p = new Point<float>;
只有(a)Point template的float執行個體、(b)new 運算子、(c)default constructor需要被“具現”。
關鍵字: malloc wxWidgets OpenGL 多態性 doxygen 《深度探索C++物件模型》讀書筆記(7)。
***Template的錯誤報表***
所有與類型相關的檢驗,如果涉及到template參數,都必須延遲到真正的具現操作發生。
對於下面的template聲明:
template <class T>
class Mumble
{
public:
Mumble(T t = 1024) : _t(t)
{
if(tt != t)
throw ex ex;
}
private:
T tt;
}
其中像“T t = 1024”、“tt != t”這樣的潛在錯誤在template聲明時並不會報告,而會在每個具現操作發生時被檢查出來並記錄之,其結果將因不同的實際類型而不同。
Mumble<int> mi; // 上述兩個潛在錯誤都不存在
Mumble<int*> pmi; // 由於不能將一個非零的整數常量指定給一個指標,故“T t = 1024”錯誤
***Template中的名稱決議方式***
區分以下兩種意義:一種是“scope of the template definition”,也就是“定義出template”的程式,另一種是“scope of the template instantiation”,也就是“具現出template”的程式。
// scope of the template definition
extern double foo(double);
template <class type>
class ScopeRules
.{
public:
void invariant() { _member = foo(_val); }
type type_dependent() { return foo(_member); }
// ...
private:
int _val;
type _member;
};
// scope of the template instantiation
extern int foo(int);
ScopeRules<int> sr0;
關鍵字: malloc wxWidgets OpenGL 多態性 doxygen 《深度探索C++物件模型》讀書筆記(7)。
在“scope of the template definition”中,只有一個foo()函式宣告位於scope之內;然而在“scope of the template instantiation”中,兩個foo()函式宣告都位於scope之內。對於以下函數操作:
// scope of the template instantiation
sr0.invariant();
那麼,在invariant()中調用的究竟是哪一個foo()函數實體呢?
Template之中,對於一個nonmember name的決議結果是根據這個name的使用是否與“用以具現出該template的參數類型”有關而決定的,如果其使用互不相關,那麼就以“scope of the template definition”來決定name,否則就以“scope of the template instantiation”來決定name.
// 因為_val的類型是int,而函數的決議只和函數原型有關,與函數傳回值無關
// 被用來具現這個template的真正類型對於_val的類型沒有影響
_member = foo(_val);
故此處的叫用作業由“scope of the template definition”來決議。
若是如下的函數調用:
sr0.type_dependent();
由於_member的類型與template參數有關,故此處由“scope of the template instantiation”來決議。
***Member Function的具現行為***
以手動方式在個別的object module中完成預先具現操作,是唯一有效率的辦法。
***執行期類型識別***
dynamic_cast運算子可以在執行期決定真正的類型。如果downcast是安全的(也就是說,一個base type pointer指向一個derived class object),這個運算子會傳回被適當轉型過的指標;如果downcast不是安全的,這個運算子會傳回0.
關鍵字: malloc wxWidgets OpenGL 多態性 doxygen 《深度探索C++物件模型》讀書筆記(7)。
typedef type *ptype;
typedef fct *pfct;
simplify_conv_op(ptype pt)
{
if(pfct pf = dynamic_cast<pfct>(pt)) {
...
}
else { ... }
}
什麼是dynamic_cast的真正成本?pfct的一個類型描述器會被編譯器產生出來,由pt指向之class object類型描述器必須在執行期通過vptr取得。下面是可能的轉換:
// 取得pt的類型描述器
((type_info*)(pt->vptr[0]))->_type_description;
其中,type_info是C++ Standard所定義的類型描述器的class名稱,該class中放置著待索求的類型資訊。virtual table的第一個slot內含type_info object的地址,此type_info object與pt所指之class type有關。
dynamic_cast運算子也適用於reference身上,然而對於一個non-type-safe-cast,其結果不會與施行於指標的情況一樣。一個reference不可以像指標那樣“把自己設為0便代表了no object”;若將一個reference設為0,會引起一個臨時性對象(擁有被參考到的類型)被產生出來,該臨時對象的初值為0,這個reference然後被設定為該臨時變數的一個別名。
因而,如果reference並不真正是某一種derived class,那麼可通過丟出一個bad_cast exception進行處理:
simplify_conv_op(const type &rt)
{
try {
fct &rf = dynamic_cast<fct&>(rt);
}
catch(bad cast) {
// ...
}
}
當然,你也可以使用typeid運算子來達到同樣的目的:
simplify_conv_op(const type &rt)
{
if(typeid(rt) == typeid(fct))
{
fct &rf = dynamic_cast<fct&>(rt);
}
else { ... }
}
系列文章:
《深度探索C++物件模型》讀書筆記(1)
《深度探索C++物件模型》讀書筆記(2)
《深度探索C++物件模型》讀書筆記(3)
《深度探索C++物件模型》讀書筆記(4)
《深度探索C++物件模型》讀書筆記(5)
《深度探索C++物件模型》讀書筆記(6)
《深度探索C++物件模型》讀書筆記 最後一記