博客 / 詳情

返回

c++ 判斷基類指針指向的真實對象類型

在 c++ 面向對象使用中,我們常常會定義一個基類類型的指針,在運行過程中,這個指針可能指向一個基類類型的對象,也可能指向的是其子類類型的對象,那現在問題來了,我們如何去判斷這個指針到底執行了一個什麼類型的對象呢?
![上傳中...]()
今天我們就聊一下這個問題,首先我們要區分是否允許 RTTI,據此有不同辦法。

1 允許使用 RTTI

在打開 rtti 的場景下,可以使用 dynamic_casttypeid 這兩個運算符來判斷對象的真實類型。

1.1 使用 dynamic_cast

dynamic_cast 用於在運行時進行多態類型檢查和轉換,它可以將指向基類的指針轉換為指向派生類的指針或引用。如果轉換成功,則説明對象屬於目標類或其派生類。如果轉換失敗,則返回空指針。
我們看如下例子,我們想判斷指針 basePtr 是否指向了 Child2 類型的對象。總共進行了兩次測試,第一次讓該指針指向了 Child1 類型的對象,第二次則是指向了 Child2 類型的對象。

#include <iostream>

class Basic {
public:
    virtual void say() {
        std::cout << "我是基類" << std::endl;
    }
};

class Child1 : public Basic {
public:
    void say() {
        std::cout << "我是 child 1" << std::endl;
    }
};

class Child2 : public Basic {
public:
    void say() {
        std::cout << "我是 child 2" << std::endl;
    }
};

int main()
{
    Basic* basePtr;
    basePtr = new Child1();
    if (dynamic_cast<Child2*>(basePtr)) {
        std::cout << "[test 1]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 1]指針沒有指向 Child2 類型對象" << std::endl;
    }

    delete basePtr;
    basePtr = new Child2();
    if (dynamic_cast<Child2*>(basePtr)) {
        std::cout << "[test 2]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 2]指針沒有指向 Child2 類型對象" << std::endl;
    }
    delete basePtr;
}

讓我們一起看看兩次的打印,這是符合我們的預期的,使用 dynamic_cast 可以判斷一個基類類型的指針是否指向了某個具體類類型。

image.png

在這裏,有的朋友會好奇,我為什麼添加了 say() 這麼一個方法,湊數嗎?確實是,就是湊數的dynamic_cast 是用於多態運行時的類型檢查,如果我不增加這麼一個方法,並且在基類中添加上 virtual 關鍵字,那就不存在多態,也就無從談起運行時多態類型檢查。下面是我將 virtual 去掉,或者乾脆刪除 say() 方法的編譯結果。

image.png

1.2 使用 typeid

typeid 運算符返回一個 type_info 對象,該對象包含類型的相關信息。通過比較兩個指針的類型信息,可以確定它們是否具有相同的類型。這裏我們不用管 type_info 是什麼東西,我們主要看看怎麼用,下面繼續看看剛剛的例子。

#include <iostream>

class Basic {
public:
    virtual void say() {
        std::cout << "我是基類" << std::endl;
    }
};

class Child1 : public Basic {
public:
    void say() {
        std::cout << "我是 child 1" << std::endl;
    }
};

class Child2 : public Basic {
public:
    void say() {
        std::cout << "我是 child 2" << std::endl;
    }
};

int main()
{
    Basic* basePtr;
    basePtr = new Child1();
    if (typeid(*basePtr) == typeid(Child2)) {
        std::cout << "[test 1]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 1]指針沒有指向 Child2 類型對象" << std::endl;
    }

    delete basePtr;
    basePtr = new Child2();
    if (typeid(*basePtr) == typeid(Child2)) {
        std::cout << "[test 2]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 2]指針沒有指向 Child2 類型對象" << std::endl;
    }
    delete basePtr;
}

運行結果,和剛剛使用 dynamic_cast 一樣。我們這裏是來判斷基類指針是否指向了某個具體類對象,typeid 當然也可以用來判斷兩個指針指向的具體類類型是否相同,這裏不再展開。

image.png

值得注意的是,使用 typeid 時,如果去掉基類方法中的 virtual 關鍵字,編譯並不會報錯,但運行結果肯定會錯,此時因為不存在多態,該運算符始終會返回基類的信息。

2 不允許使用 RTTI

出於某些原因,你的項目可能禁用了 RTTI,那這個時候我們應該怎麼判斷基類指針指向的具體類呢?我們還能利用多態本身,就是給基類新增一個虛方法,子類在必要的時候來重寫。

下面我們繼續用剛剛的例子,一起看看代碼吧。

#include <iostream>

class Basic {
public:
    virtual void say() {
        std::cout << "我是基類" << std::endl;
    }
    virtual bool isChild2() {
        return false;
    }
};

class Child1 : public Basic {
public:
    void say() {
        std::cout << "我是 child 1" << std::endl;
    }
};

class Child2 : public Basic {
public:
    void say() {
        std::cout << "我是 child 2" << std::endl;
    }
    bool isChild2() {
        return true;
    }
};

int main()
{
    Basic* basePtr;
    basePtr = new Child1();
    if (basePtr->isChild2()) {
        std::cout << "[test 1]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 1]指針沒有指向 Child2 類型對象" << std::endl;
    }

    delete basePtr;
    basePtr = new Child2();
    if (basePtr->isChild2()) {
        std::cout << "[test 2]指針指向了 Child2 類型對象" << std::endl;
    } else {
        std::cout << "[test 2]指針沒有指向 Child2 類型對象" << std::endl;
    }
    delete basePtr;
}

我們新增了一個 isChild2() 的方法,用來判斷該類是否是 Child2 類型,因為我這裏只需要判斷基類指針是否指向了 Child2 類型的對象,所以就直接增加了個 bool 返回值的接口進行判斷了。在實際使用時,也可以返回枚舉變量,分別對應例子中的三個類。

3 總結

當項目允許 RTTI 時,我們可以使用 dynamic_casttypeid 運算符來判斷一個基類指針指向的具體對象類型;當禁用 RTTI 時,我們就利用多態本身,為基類新增一個方法,用來獲取類類型信息。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.