程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 《深度探索C++對象模型》讀書筆記(7)

《深度探索C++對象模型》讀書筆記(7)

編輯:關於C++

***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需要被“具現”。

***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;

在“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.

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 { ... }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved