簡體   English   中英

C ++ STL容器中的多態性

[英]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.

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