發新話題

C++ Gossip - 物件導向《多型》 執行時期型態資訊

C++ Gossip - 物件導向《多型》 執行時期型態資訊

在C++這種可以進行多型機制的語言中,您可以將基底類別指標指向衍生類別物件,這種指定通常在執行時期後發生,您並無法在編譯時期即得知指標所指向的物件型態,而必須在執行時期取得物件的執行時期資訊。

RTTI 全名為Run-Time Type Information,也有人作Run-Time Type Identification,C++中用來取得指標或參考所實際代表的物件,您可以使用typeid()來取得物件於執行時期的資訊,要使用 typeid(),您必須包括<typeinfo>標頭檔,typeid()使用時傳入一個物件:
typeid(object);


typeid()會傳回一個type_info物件,其擁有幾個成員可以描述或進行物件的比較:
const char *name(); // 取得物件型態名稱
bool before(const type_info &ob);  // 當物件的名稱順序位於ob之前時,傳回true
bool operator==(const type_info &ob);  // 比較物件型態是否相同
bool operator!=(const type_info &ob); // 比較物件型態是否不同


==與!=運算子在這邊被重載為可以比較兩個物件的型態是否相同;typeid()也可以使用型態名稱作為引數,這通常是用來取得一個type-info物件,並與一個物件作比較時使用:
typeid(type-name);


用範例來說明typeid()與其成員的使用方法與應用,下面這個程式只是使用name()取得物件的型態名稱:

#include <iostream>
#include <typeinfo>
using namespace std;

class Base {
public:
    virtual void foo() {
        cout << "Base" << endl;
    }
};

class Derived1 : public Base {
public:
    void foo() {
        cout << "Derived1" << endl;
    }
};

class Derived2 : public Base {
public:
    void foo() {
        cout << "Derived2" << endl;
    }
};

int main() {
    Base *ptr;  // 基底類別指標
    Base base;
    Derived1 derived1;
    Derived2 derived2;

    ptr = &base;
    cout << "ptr 指向 "
         << typeid(*ptr).name()  
         << endl;

    ptr = &derived1;
    cout << "ptr 指向 "
         << typeid(*ptr).name()
         << endl;

    ptr = &derived2;
    cout << "ptr 指向 "
         << typeid(*ptr).name()
         << endl;
   
    return 0;
}
執行結果:
ptr 指向 4Base
ptr 指向 8Derived1
ptr 指向 8Derived2

您使用共同的基底類別指標來指向基底類別物件與衍生類別物件,雖然如此,還是利用typeid()取回的type-info物件仍可以得知物件的型態名稱。

RTTI的使用時機之一,就是當您將物件以參考方式傳遞給函式時,函式的參數使用共同的基底類別指標或參考,但在函式中有必須操作衍生類別中的某個方法,由於函式事先並不知道您傳入的物件型態名稱,所以您必須利用RTTI來進行判斷,下面的程式是個簡單的例子:

#include <iostream>
#include <typeinfo>
using namespace std;

class Base {
public:
    virtual void foo() = 0;
};

class Derived1 : public Base {
public:
    void foo() {
        cout << "Derived1" << endl;
    }
   
    void showOne() {
         cout << "Yes! It's Derived1." << endl;
    }
};

class Derived2 : public Base {
public:
    void foo() {
        cout << "Derived2" << endl;
    }
   
    void showTwo() {
         cout << "Yes! It's Derived2." << endl;
    }
};

void showWho(Base *base) {
    base->foo();
     
    if(typeid(*base) == typeid(Derived1)) {
        Derived1 *derived1 = static_cast<Derived1*>(base);
        derived1->showOne();
    }
    else if(typeid(*base) == typeid(Derived2)) {
        Derived2 *derived2 = static_cast<Derived2*>(base);
        derived2->showTwo();   
    }
}

int main() {
    Derived1 derived1;
    Derived2 derived2;

    showWho(&derived1);
    showWho(&derived2);
   
    return 0;
}
執行結果:
Derived1
Yes! It's Derived1.
Derived2
Yes! It's Derived2.

傳統的C風格轉型語法也是可以使用的,例如:
Derived1 *derived1 = (Derived1*) base;


當然不建議使用這種方式強制轉型,事實上使用static_cast也不是很適合,C++為了支援RTTI還提供有dynamic_cast,這在下一個主題中介紹。

TOP

發新話題

本站所有圖文均屬網友發表,僅代表作者的觀點與本站無關,如有侵權請通知版主會盡快刪除。