類型標識符(typeid)
標準C++的一個新特徵是RTTI(Run-Time Type Information運行時類型資訊),它為程式在運行時確定物件類型,提供了一種標準方法。在標準C++中,有三個支援RTTI的元素:
<!--[if !supportLists]-->1. <!--[endif]-->關鍵字dynamic_cast(動態強制轉換):操作符dynamic_cast將一個指向基類的指標轉換為一個指向衍生類別的指標(如果不能正確轉換,則返回0——null 指標),格式為:
dynamic_cast < type-id > ( exdivssion )
dynamic_cast在轉化過程中要用到相關類的類型資訊類type_info中的資訊。該關鍵字在前面的小小節1.2)中已經介紹過。
<!--[if !supportLists]-->2. <!--[endif]-->關鍵字typeid(類型標識符):用來檢測指標類型(返回type_info類對象的指標),格式為:
typeid ( exdivssion ) 或 typeid ( type-id )
其中,exdivssion為結果為對象的運算式,type-id為類名。
<!--[if !supportLists]-->3. <!--[endif]-->類type_info(類型資訊):儲存特定類型的有關資訊,定義在<typeinfo>標頭檔中。type_info類的具體內容由編譯器實現來決定,但是至少必須包含返回字串的name()成員函數。下面是type_info類的VC05實現版本:
class type_info { // VC05中定義的簡化
public:
virtual ~type_info();
bool operator==(const type_info& rhs) const;
bool operator!=(const type_info& rhs) const;
int before(const type_info& rhs) const;
const char* name() const;
const char* raw_name() const;
private:
void *_m_data;
char _m_d_name[1];
type_info(const type_info& rhs);
type_info& operator=(const type_info& rhs);
static const char *_Name_base(const type_info *prhs, __type_info_node* __ptype_info_node);
static void _Type_info_dtor(type_info *prhs);
};
例如:(可建立一個名為tmp的“Visual C++/常規/空項目”型項目,將如下兩個檔案加入到該項目中)
// tmp.h
template<class T> class A { };
// tmp.cpp
#include <typeinfo.h>
#include <iostream>
#include "tmp.h"
using namespace std;
int main( ){
A<int> a;
A<char> b;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
if (typeid(a) == typeid(b)) cout << "a==b" << endl;
else cout << "a!=b" << endl << endl;
cout << endl;
int i;
cout << typeid(int).name() << endl;
cout << typeid(i).name() << endl;
if (typeid(int) == typeid(i)) cout << "typeid(int) = typeid(i)" << endl;
else cout << "typeid(int) != typeid(i)"<< endl;
cout << endl;
}
輸出結果為:
class A<int>
class A<char>
a!=b
int
int
typeid(int) = typeid(i)
注意:只有對包含虛函數的抽象類別層次,使用RTTI才有實際意義。
8)類型名(typename)
對於有的嵌套類中的標識符,本來應該作為類型來處理,但是編譯器並不知道這一點,而可能把它當成了靜態變數。
對模板中出現的一個標識符,若編譯器既可以把它當作一個類型,又可以把它視為一個變數、對象、枚舉、函數或模板時,則編譯器一般不會認為這個標識符是類型,而認為它是一個其他元素(例如是變數或對象)。
解決辦法是,使用標準C++新增加的關鍵字typename,來明確告訴編譯器,它後面的標識符是一個類型名,而不是其他什麼東西。
例如:
template<class T> class X {
typename T::id i; // 如果沒有typename來說明,編譯器會將T::id當成靜態變數
public:
void f ( ) { i.g( ); }
};
class Y {
public:
class id {
public:
void g( ) { }
};
};
int main ( ) {
X<Y> xy;
xy.f ( );
}
最後一種用法是說,可以用typename來代替模板聲明中的型別參數class,即:可將
template<class T> ……
改為
template<typename T> ……
而且這樣更名符其實。因為除了類類型外,基礎資料型別 (Elementary Data Type)和結構等類型,也是可以作為模板的型別參數的。
例如:(能夠列印任意標準C++序列容器中的資料的函數模板)
// PrintSeq.cpp
#include <iostream>
#include <list>
#include <memory>
#include <vector>
using namespace std;
template<class T, template<class U, class = allocator<U> > class Seq>
void printSeq(Seq<T>& seq) {
for (typename Seq<T>::iterator b = seq.begin(); b != seq.end(); b++)
cout << *b << endl;
}
int main ( ) {
// 處理向量
vector<int> v;
v.push_back(1); v.push_back(2);
printSeq(v);
// 處理表
list<int> lst;
lst.push_back(3); lst.push_back(4);
printSeq(lst);
}
輸出為:
1
2
3
4
注意:關鍵字typename並不能建立一個新類型名,它只是通知編譯器,將標識符解釋為類型。若想建立一個新類型名,你可以使用關鍵字typedef。例如
typename Seq<T>::iterator It; // 告訴編譯器iterator是類型,It是該類型的變數
typedef typename Seq<T>::iterator It; // 建立了一個與iterator等價的新類型名It