![](/img/trans.png)
[英]C++ How to access derived class member from base class pointer without using a static_cast or dynamic_cast?
[英]What's wrong with using dynamic_cast or static_cast when accessing known derived class objects stored in vector<base*>?
這是我在這里提出的問題的后續: 更新向量中同一基類的不同派生類的數據成員
我正在用C ++構建3D網格化模型,該模型具有不同的單元格類型,所有單元格類型都存儲在Grid類的向量中。 我定義了一個基本的GridCell類,也有幾個派生類GridCell1 , GridCell2 , GridCell3等等。
我將一個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.