c++之巧用type_info


注冊博客有一段時間,一直很忙沒有寫技術文章,今天把近期心得寫出來一起分享。

c++沒有反射機制,我們可以通過c++的語言特性去實現同樣的功能。本文介紹使用type_info實現的一些發射特性。

1 type_info簡介:

class type_info {
public:
_CRTIMP virtual ~type_info();
_CRTIMP int operator==(const type_info& rhs) const;
_CRTIMP int operator!=(const type_info& rhs) const;
_CRTIMP int before(const type_info& rhs) const;
_CRTIMP const char* name() const;
_CRTIMP 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);
};

(注: c++0x01增加了hash_code方法,獲取類名對應的hash值)

使用type_info需要注意3點:

(1)type_info的構造函數和賦值操作符都為私有。因此不要試圖去定義或復制一個type_info對象。創建type_info對象的唯一方法是使用typeid操作符。如果想使用type_info的引用,可以使用const type_info& info = typeid(foo)。

(2) type_info::name返回的是類似於"class CBase*"之類的名字, 而且c++標准只是告訴編譯器需要實現type_info::name函數,不同編譯器編譯運行后輸出不一樣。

(3) 對於父類指針類型變量,typeid(ptr).name不具有多態性, 仍返回此父類的指針類名,如"class CBase*"。如果直接傳入對象如typeid(*ptr).name則具有多態性,將返回"class CDevievd"類似的子類類名。  

2 巧用type_info

(1) 得到類名的一種方法是在類中實現class_name()方法,如 

class CDataType 
{
public:
virtual char* class_name() { return "CDataType"; }
};

這種方法的一個缺點就是強迫每一個類必須實現這個函數,否則后續的工作就無法完成。

 

下面是一個為數據注冊創建者,並根據數據類型獲取創建者的例子,看一下它是如何工作的。

// 數據
class CDataType
{
public:
virtual char* class_name() { return "CDataType"; }
};
class CDataTypeA : public CDataType
{
public:
virtual char* class_name() { return "CDataTypeA"; }
};
class CDataTypeB : public CDataType
{
public:
virtual char* class_name() { return "CDataTypeB"; }
}

// 數據創建者
class CreatorType {};
class CreatorForDataA : public CreatorType {};
class CreatorForDataB : public CreatorType {};

typedef std::string KeyType;
static std::map<std::string, CreatorType*> nameMap;

void RegistCreator(KeyType key, CreatorType* creator)
{
nameMap[key] = creator;
}

CreatorType* GetCreator( CDataType* data )
{
return nameMap[data->class_name()];
}

void main()
{
RegistCreator( "CDataTypeA", new CreatorForDataA );
RegistCreator( "CDataTypeB", new CreatorForDataB );

CDataTypeA* dataA = new CDataTypeA;

CreatorType* creator = GetCreator(dataA);
}

 

為每個數據強制加一個class_name虛函數看起來是否有點冗余?

下面我們用type_info來實現相同的功能。

// 數據
class CDataType {};
class CDataTypeA : public CDataType {};
class CDataTypeB : public CDataType {};

CreatorType* GetCreator( CDataType* data )
{
return nameMap[typeid(*data).name()];
}

void main()
{
RegistCreator( "CDataTypeA", new CreatorForDataA );
RegistCreator( "CDataTypeB", new CreatorForDataB );

CDataTypeA* dataA = new CDataTypeA;

CreatorType* creator = GetCreator(dataA);
}

比較一下,這個版本的代碼要簡單許多了,也有較好的擴展性,或許我們可以放松一下了。

可是沒過多少天問題就來了,有的同事擔心大量的字符串比較會影響速度,雖然map是很高效的。擔心確實是不必要的,殺手鐧還在你的手中——hash_code。

hash_code是把字符串映射到一個唯一整數,使用整數作為map鍵值效率要比string高許多。廢話不說,上代碼。

typedef int KeyType;
static std::map<KeyType, CreatorType*> nameMap;

void RegistCreator(KeyType key, CreatorType* creator)
{
nameMap[key] = creator;
}

CreatorType* GetCreator( CDataType* data )
{
return nameMap[typeid(*data).hash_code()];
}

void main()
{
RegistCreator( typeid(CDataTypeA).hash_code(), new CreatorForDataA );
RegistCreator( typeid(CDataTypeB).hash_code(), new CreatorForDataB );

CDataTypeA* dataA = new CDataTypeA;

CreatorType* creator = GetCreator(dataA);
}


如果你的c++版本不夠,發現type_info根本沒有hash_code方法,沒有關系,可以使用下面的方法代替。

size_t hash_code( const type_info& info )
{
// hash name() to size_t value by pseudorandomizing transform
const char *_Keyval = info.name();
size_t _Val = 2166136261U;
size_t _First = 0;
size_t _Last = _CSTD strlen(_Keyval);
size_t _Stride = 1 + _Last / 10;

for(; _First < _Last; _First += _Stride)
_Val = 16777619U * _Val ^ (size_t)_Keyval[_First];
return (_Val);
}

 

本文只是拋磚引玉,希望各位朋友能有更多的發現。

 

2012-03-16

http://www.cnblogs.com/dario/

<轉載請注明作者和出處>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM