1. 程式人生 > >C++ Primer第五版筆記——執行時型別識別

C++ Primer第五版筆記——執行時型別識別

執行時型別識別(run-time type identification,RTTI)的功能由兩個運算子實現:  typeid運算子,用於返回表示式的型別。  dynamic_cast運算子,用於將基類的指標或引用安全的轉換成派生類的指標或引用。  當這兩個運算子用於某種型別的指標或引用,並且該型別含有虛擬函式時,運算子將使用指標或引用所繫結的物件的動態型別。  這兩個運算子適用於以下情況:使用基類物件的指標或引用執行某個派生類的操作並且該操作不是虛擬函式(在可能的情況下還是優先使用定義虛擬函式的方式)。

dynamic_cast運算子

使用形式:

dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)

其中type必須是一個類型別,並且通常情況下該型別應該有虛擬函式。第一個形式中,e必須是一個有效的指標;第二個形式中,e必須是一個左值;第三個形式中,e不能一個左值。 在上面的所有形式中,e的型別必須符合以下三個條件中的任何一個: 1、e的型別與type的型別相同; 2、e的型別與type的公有派生類的型別相同; 3、e的型別與type的公有基類的型別相同。 符合條件的情況下,型別轉換可以成功;否則會失敗。如果一條dynamic_cast語句的轉換目標是指標型別並且失敗了,則結果為0;如果轉換目標是引用型別並且失敗了,則會丟擲bad_cast異常。

指標型別的dynamic_cast   假定Base類至少含有一個虛擬函式,Derived是Base的公有派生類。如果有一個Base的指標bp,則我們可以在執行時將它轉換成指向Derived的指標:

if(Derived* dp = dynamic_cast<Derived*>(bp)){
	//。。。
}else{
	//。。。
}

如果bp指向Derived物件,則上述型別轉換初始化dp並令其指向bp指向的Derived物件。型別轉換失敗的情況下,其結果為0,dp為0意味著if條件失敗。 (此處的dp是區域性變數,這樣做的好處是可以在一條語句中完成型別轉換和條件檢查兩項任務,且dp在if外部不會被訪問到)。

引用型別的dynamic_cast   引用型別的dynamic_cast與指標型別的dynamic_cast在表示錯誤發生的方式上略有不同。因為不存在空引用,因此當對引用型別轉換失敗時,程式丟擲一個名為std::bad_cast的異常,該異常定義在typeinfo標準庫標頭檔案中。

void f(const Base& b){
	try{
		const Derived& d = dynamic_cast<const Derived&>(b);
		//。。。
	}catch(bad_cast){
	//。。。
	}
}

typeid運算子

該運算子允許程式向表示式提問:你的物件時什麼型別?其表示式形式如:typeid(e)。其中e可以是任意表達式或者型別的名字。其結果是一個常量物件的引用,該物件的型別是標準庫型別type_info或type_info的公有派生型別(該類定義在typeinfo標頭檔案)。

使用typeid運算子   通常情況下,使用該運算子來比較兩個表示式的型別是否相同,或者比較一條表示式與指定型別是否相同:

Derived* dp = new Derived;
Base* bp = dp;						//兩個指標都指向Derived物件
//在執行時比較兩個物件的型別
if(typeid(*dp) == typeid(*bp)){
	//。。。
}
//檢查執行時型別是否和指定型別相同
if(typeid(*bp) == typeid(Derived)){
	//。。。
}

第一個if語句,比較bp和dp所指向的物件的動態型別是否相同,第二個if判斷bp指向的物件是否是Derived物件。

typeid是否需要執行時檢查決定了表示式是否會被求值,只有當型別含有虛擬函式時,編譯器才會對錶達式求值。反之,如果型別不含有虛擬函式,則typeid返回表示式的靜態型別(編譯器無需對錶達式求值也能知道表示式的靜態型別)。

例子

class Base{
	friend bool operator==(const Base&,const Base&);
public:
	//Base介面成員
protected:
	virtual bool equal(const Base&) const;
	//Base的資料成員和其他用於實現的成員
};	
class Derived:public Base{
public:
	//Derived的介面成員
protected:
	bool equal(const Base&) const;
	//Derived的資料成員和其他用於實現的成員
};

bool operator==(const Base& lhs,const Base& rhs){
	//如果typeid不相同,返回false,否則虛呼叫equal
	return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}

bool Derived::equal(const Base& rhs) const{
	auto r = dynamic_cast<const Deived&>(rhs);
	//。。。
}
bool Base::equal(const Base& rhs) const{
	//。。。
}