簡體   English   中英

訪問存儲在向量中的已知派生類對象時,使用dynamic_cast或static_cast有什么問題 <base*> ?

[英]What's wrong with using dynamic_cast or static_cast when accessing known derived class objects stored in vector<base*>?

這是我在這里提出的問題的后續: 更新向量中同一基類的不同派生類的數據成員

我正在用C ++構建3D網格化模型,該模型具有不同的單元格類型,所有單元格類型都存儲在Grid類的向量中。 我定義了一個基本的GridCell類,也有幾個派生類GridCell1GridCell2GridCell3等等。

我將一個GridCell對象傳遞到模型過程中。 傳遞的GridCell對象類型(派生類)將與接收模型過程一起使用。 換句話說,模型過程設計模型過程(可能是錯誤地)的,它們具有不同的功能,希望對適當的派生對象的數據成員起作用。 當然,它們看不到,因為對象是從std::vector<gridCell*> gridCellVector因此它僅將對象視為指向基類的指針。 此時,我有兩個選擇:1)重寫我的模型過程,2)使用static_cast或dynamic_cast向下轉換傳遞的對象。

問題:在這種情況下,當我的模型過程(僅在正確的GridCell對象類型上運行)知道它們正在接收的派生類時,應該使用static_cast還是dynamic_cast? 人們似乎真的不喜歡使用這些工具。 我為什么要遠離使用這些東西?

有時強制轉換是不可避免的,因為多態不能解決所有問題。 但是,實際上設計多態是為了消除對類型的了解 如果需要了解類型,則必須詢問多態性是否是適合該工作的工具,否則是否無法正確實現多態性。

理想的情況是每個對象都知道自己的行為,因此在處理通用類型時, if/else任何人都無需做出if/else決策。

為此,您通常會將處理代碼放在類型本身內部,而不是使用外部處理例程來為接收到的每個對象做出if/else決策。

因此,盡管鑄造有時並不是必須的,但並不是天生就不好,但如果您需要使用它,也可能是設計不良的征兆。

問題:在這種情況下,當我的模型過程(僅在正確的GridCell對象類型上運行)知道它們正在接收的派生類時,應該使用static_cast還是dynamic_cast? 人們似乎真的不喜歡使用這些工具。 我為什么要遠離使用這些東西?

如果您真的知道類型,請使用static_cast (速度更快),否則請使用dynamic_cast

鑄造的替代方法:

您可以使用的一種“技巧”(實際上是轉換)是使用多態來為您選擇正確的類型。

您可以做的是向基類GridCell中添加一個虛函數 ,該虛函數采用分別處理子類型的類的對象 ,並獲取覆蓋的子類型來自己調用相關的處理函數。 這樣, 類型固有地知道如何處理它。

一個例子

class Processor
{
public:

    void process_1(class GridCell1* cell)
    {
        std::cout << "processing type 1\n";
    }

    void process_2(class GridCell2* cell)
    {
        std::cout << "processing type 2\n";
    }
};

class GridCell
{
public:
    virtual ~GridCell() {}

    virtual void process(Processor& proc) = 0;
};

class GridCell1
: public GridCell
{
public:
    void process(Processor& proc) override
    {
        proc.process_1(this);
    }
};

class GridCell2
: public GridCell
{
public:
    void process(Processor& proc) override
    {
        proc.process_2(this);
    }
};

class Grid
{
    std::vector<GridCell*> gridCellVector;

public:
    Grid()
    {
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell2);
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell2);
        gridCellVector.push_back(new GridCell1);
    }

    ~Grid() { for(auto cell: gridCellVector) delete cell; }

    void process()
    {
        Processor proc;
        for(auto cell: gridCellVector)
            cell->process(proc);
    }
};

int main ()
{
    Grid grid;

    grid.process();
}

輸出:

processing type 1
processing type 1
processing type 2
processing type 1
processing type 2
processing type 1

在所描述的情況下使用static_cast <>是不安全的,因為所描述的強制轉換是對未知派生類的向下轉換,而為什么不使用static_cast的最合適的解釋是:

如果new_type是指向某個類D的指針或引用,而表達式的類型是指向其非虛擬基數B的指針或引用,則static_cast執行向下轉換。 這種static_cast不會進行任何運行時檢查以確保對象的運行時類型實際上是D,並且只有在通過其他方式(例如實現靜態多態性)保證了此前提條件的情況下,才可以安全地使用該對象。 安全下調可以通過dynamic_cast完成。 (了解有關static_cast轉換的更多信息)

如果不同的對象(gridCell1,gridCell2)具有不同的新函數,例如f1()和f2(),則它們不會作為虛擬函數出現在基類中-不可能使用c ++的多態機制,因為編譯器只能在編譯期間看到基礎對象函數,而不能訪問派生類的函數。

但是,您可以添加一種機制來幫助您使用RTTI在運行時確定對象類型。 通過使基類抽象化,您將為每個派生類創建一個vtbl,並且可以嘗試對每個派生對象進行dynamic_cast。 如果強制轉換失敗,它將返回null,您將繼續嘗試其他派生類,直到成功為止,然后可以使用所需的函數。

例:

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

class DerivedA : public Base
{
    public:
    virtual void f() 
    {
        cout << "DerivedA::f()";    
    }
    void f1()
    {
        cout << "DerivedA::f1()";
    }
};

class DerivedB : public Base
{
    public:
    virtual void f() 
    {
        cout << "DerivedB::f()";    
    }
    void f2()
    {
        cout << "DerivedB::f2()";
    }
};

void foo(Base* baseObj)
{
    DerivedA* a = dynamic_cast<DerivedA*>(baseObj);
    if (a)
    {
        a->f1();
        return;
    }
    DerivedB* b = dynamic_cast<DerivedB*>(baseObj);
    if (b)
    {
        b->f2();
        return;
    }
}

int main() {
    DerivedB* bObj = new DerivedB();
    foo(bObj); //will print DerivedB::f2()
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM