1. 程式人生 > >39、不一樣的C++系列--型別識別

39、不一樣的C++系列--型別識別

型別識別

  • 在面向物件中可能出現下面的情況
    • 基類指標指向子類物件
    • 基類引用成為子類物件的別名
//Base* Base& 靜態型別
//Derived 動態型別
Base* p = new Derived();
Base& r = *p;

型別區別:

  • 靜態型別
    • 變數(物件)自身的型別
  • 動態型別
    • 指標(引用)所指向物件的實際型別
void test(Base* b)
{
    //危險的轉換方式
    Derived* d = static_cast<Derived *>(b);
}

在上述程式碼中,基類指標是否可以強制型別轉換為子類指標取決於動態型別!

在C++中是利用多型來得到動態型別

  1. 在基類中定義虛擬函式返回具體的型別資訊
  2. 所有的派生類都必須實現型別相關的虛擬函式
  3. 每個類中的型別虛擬函式都需要不同的實現

這裡舉個例子:

#include <iostream>
#include <string>

using namespace std;

//定義基類
class Base
{
public:
    //基類中的虛擬函式
    virtual string type()
    {
        return "Base";
    }
};

//定義子類 繼承基類
class Derived : public
Base { public: string type() { return "Derived"; } void printf() { cout << "I'm a Derived." << endl; } }; //定義子類 繼承基類 class Child : public Base { public: string type() { return "Child"; } }; void test(Base* b) { /* 危險的轉換方式 */
// Derived* d = static_cast<Derived*>(b); //這裡最安全的辦法就是使用類成員函式來返回型別,然後來判斷 if( b->type() == "Derived" ) { Derived* d = static_cast<Derived*>(b); d->printf(); } // cout << dynamic_cast<Derived*>(b) << endl; } int main(int argc, char *argv[]) { Base b; Derived d; Child c; test(&b); test(&d); test(&c); return 0; }

執行結果為:

I'm a Derived.

上述的解決方案存在一些缺陷:

  • 必須從基類開始提供型別虛擬函式
  • 所有的派生類都必須重寫型別虛擬函式
  • 每個派生類的型別名必須唯一

那有沒有另外一種可以完善這些缺點的方案呢?有的,這裡我們來學習一個新的知識點:

  • C++提供了typeid關鍵字用於獲取型別資訊
    • typeid關鍵字返回對應引數的型別資訊
    • typeid返回一個type_info類物件
    • 當typeid的引數為NULL時將丟擲異常
  • typeid 關鍵字的使用
int i = 0;

const type_info& tiv = typeid(i);
const type_info& tii = typeid(int);

cout << (tiv == tii) << endl;
  • typeid 的注意事項
    • 當引數為型別時:返回靜態型別資訊
    • 當引數為變數時:
      • 不存在虛擬函式表 —- 返回靜態型別資訊
      • 存在虛擬函式表 —- 返回動態型別資訊

使用示例:

#include <iostream>
#include <string>
#include <typeinfo>

using namespace std;

//定義基類
class Base
{
public:
    //解構函式為虛擬函式
    virtual ~Base()
    {
    }
};

class Derived : public Base
{
public:
    void printf()
    {
        cout << "I'm a Derived." << endl;
    }
};

void test(Base* b)
{
    const type_info& tb = typeid(*b);

    //tb為type_info類,使用name成員函式可以得到型別名稱
    cout << tb.name() << endl;
}

int main(int argc, char *argv[])
{
    int i = 0;

    //這裡使用typeid來獲取型別
    const type_info& tiv = typeid(i);
    const type_info& tii = typeid(int);

    cout << (tiv == tii) << endl;

    Base b;
    Derived d;

    test(&b);
    test(&d);

    return 0;
}

輸出結果為:

1
4Base
7Derived