[英]Polymorphism in C++ STL containers
我想知道這個問題是否存在優雅的解決方案。
假設如下:
class Base {
private:
int data;
protected:
Base(int data) : data(data) { }
int getData() const { return data; }
virtual bool someOp() = 0;
}
class Derived1 : public Base {
private:
int data2;
public:
Derived1(int data, int data2) : Base(data), data2(data2) { }
int getData2() const { return data2; }
bool someOp() override { /* something */ }
}
class Derived2 : public Base {
private:
float data2;
public:
Derived1(int data, float data2) : Base(data), data2(data2) { }
float getData2() const { return data2; }
bool someOp() override { /* something */ }
}
並且假設我對層次結構具有完全控制權,因此我可以假設Base
不會被擴展,也不會有任何DerivedX
類。
現在我想將它們存儲在std::vector
,如果我想使用多態,我會被迫存儲指針,否則對象切片將阻止我存儲其他派生屬性。 所以我基本上被迫使用std::vector<unique_ptr<Base>>
。
但是我們假設我需要存儲大量這些對象,我不想浪費雙堆分配(內部std::vector
+對象本身),同時我可以假設:
sizeof(DerivedX)
不會比sizeof(Base)
所以我想知道是否有一種優雅的方法來保持多態性並避免存儲指針。 我可以想到一些類似的解決方案
class Base {
enum {
DERIVED1,
DERIVED2
} type;
int data;
union {
int derived1data;
float derived2data;
}
bool someOp() {
if (this->type == DERIVED1) ...
else if ..
}
}
但這顯然不優雅。 我還可以嘗試利用如下事實:如果sizeof(Derived) == sizeof(Base)
通過在Base
使用受保護的聯合並從Derived
訪問它並將地址轉換為std::vector
元素來實現到期望的類型(根據枚舉),但這聽起來也很難看。
鍵入擦除和小緩沖區優化。
您可以在C ++中鍵入幾乎任何屬性,創建一個“知道”如何將屬性應用於現在未知類型的自定義接口。
boost::any
類型都會刪除以復制,銷毀,獲取typeid和強制轉換為精確匹配類型。 std::function
類型擦除以復制,使用特定簽名調用,銷毀和反向轉換為相同類型(最后很少使用)。
基於免費存儲的類型擦除實現通過交換指針來“免費”移動語義。
在您的情況下,您將需要在類型中創建“足夠大”的對齊存儲。 您需要鍵入erase down to copy,get-as-reference-to-base,destroy並可能移動(因為您在內部存儲)。
std::aligned_storage
適用於您的任務(您可以傳遞要存儲的所有類型的對齊要求)。 然后就地新建對象。
通過void*
- 復制,移動,銷毀和轉換為base*
構建要在對象上執行的操作的表。
template<class Sig>using f = Sig*;
struct table {
f<base*(void*)> convert_to_base;
f<base const*(void const*)> convert_to_const_base;
f<void(void*,void const*)> copy_construct;
f<void(void*)> destroy;
f<void(void*,void*)> move_construct;
};
template<class T>
table const* get_table() {
static table t = {
// convert to base:
[](void*p)->base*{
T*t=static_cast<T*>(p);
return t;
},
// convert to const base:
[](void const*p)->base const*{
T const*t=static_cast<T const*>(p);
return t;
},
// etc
};
return &t;
}
現在將get_table<T>()
存儲在類型擦除的實例中(它基本上是一個虛函數表,手動實現),並編寫包裝常規類以使用table
的函數來操作aligned_storage<?...>
。
現在,通過使用boost::variant
,或者像我的某些類型,就像沒有使用堆存儲那樣的any
類型,可以更容易地做到這一點。 一些鏈接包括編譯上面的偽虛函數表技術的實現。 我可能錯誤地使用了對齊存儲,所以要小心。
您可以使用std :: aligned_storage來包裝您的類。 假設Derived2是最大的:
class Storage
{
public:
Storage(int data, int data2)
{
new (&data) Derived1(data, data2);
}
Storage(int data, float data2)
{
new (&data) Derived2(data, data2);
}
Base* getBase()
{
return reinterpret_cast<Base*>(&data);
}
~Storage()
{
getBase()->Base::~Base();
}
private:
std::aligned_storage<sizeof(Derived2)> data;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.