2012-07-23 26 views
1

我需要存儲Base類型的對象以及派生類型BaseDerivedA和BaseDerivedB。這些對象需要在內存中對齊。我想提供一個遍歷所有對象的迭代器。我想避免存儲基本指針向量的內存開銷。對基類型和/或從基類派生的類型的向量進行迭代

爲此我已經建立了以下容器

struct Container { 
    std::vector<Base> bases; 
    std::vector<BaseDerivedA> derivedAs; 
    std::vector<BaseDerivedB> derivedBs; 

    // Iterator over the three vectors 
    all_iterator<Base> all_begin(){ return all_iterator(bases[0],this); } 
    all_iterator<Base> end_begin(){ return all_iterator(nullptr,this); } 

    // Where all_iterator is defined as 
    template < class T > 
    struct all_iterator 
    : public boost::iterator_facade< all_iterator<T>, 
            T, boost::forward_traversal_tag> 
    { 
     all_iterator() : it_(0) {} 
     explicit all_iterator(T* p, Container* c) // THIS JUST FEELS WRONG 
     : it_(p), c_(c) { } 

    private: 
     friend class boost::iterator_core_access; 
     T* it_; 
     Container* c_; 
     void increment() { 
      if (it_ == static_cast<T*>(&(c_->bases[c_->bases.size()-1]))) { 
       it_ = static_cast<T*>(&(c_->derivedAs[0])); 
      } else if (it_ == static_cast<T*>(&(c_->derivedAs[ds_->derivedAs.size()-1]))) { 
       it_ = static_cast<T*>(&(c_->derivedBs[0])); 
      } else if (it_ == static_cast<T*>(&(c_->derivedBs[ds_->derivedBs.size()-1]))) { 
       it_ = nullptr; // THIS DOES ALSO FEEL WRONG 
      } else { 
       ++it_; 
      } 
     } 
     bool equal(all_iterator const& other) const { 
      return this->it_ == static_cast<T*>(other.it_); 
     } 
     T& dereference() const { return *it_; } 
    }; 

我使用的是nullptr作爲一個過去的最末端迭代器以及很多演員。我還將我的迭代器傳遞給數據結構的指針。

是否有更好的方法來迭代三個向量包含基類型或基類派生?

+3

你隱藏了大部分重要的代碼,例如什麼是'all_iterator'(它是如何定義的?)以及它如何使用它的參數。你的代碼的當前實現(不管'all_iterator')如何展現未定義的行爲:你不能訪問derivedBs [derivedBs.size()],因爲這是一個超越結尾的元素。 – 2012-07-23 14:13:00

+1

「我不能認爲derivedBs結尾的人不會是derivedAs的第一個元素。」 - 這是爲什麼?這是什麼意思,考慮到不同容器的迭代器是不可比較的? – AnT 2012-07-23 14:23:05

+2

儘管如此,該代碼展現出未定義的行爲,這意味着所有投注都關閉。該方法是不正確的,你應該嘗試找到一個*解決方案*,而不是試圖找到如何使未定義的行爲工作。 – 2012-07-23 14:27:05

回答

0

爲什麼它會位於derivedAs.end()?您將永遠不會通過derivedAs訪問/修改它。所以你根本不需要這個假設。

典型的代碼將是

for(auto it = derivedAs.begin(); it != derivedAs.end(); ++it) { 
    *it = // do whatever, will never do *derivedAs.end() 
} 
+0

位於derivedBs.end()的內容很重要。如果derivedBs.end()== derivedAs.begin()循環中斷。 – gnzlbg 2012-07-23 14:14:05

+4

@gnzlbg比較從不同容器獲得的迭代器是非法的。 – ecatmur 2012-07-23 14:17:56

+0

@ecatmur由於這個原因,我提供了自己的迭代器。 – gnzlbg 2012-07-23 14:24:02

1

對於你的迭代器是正確的,你將不得不知道哪個矢量你正在穿越,這樣就可以正確地進行比較。您可以通過具有枚舉告訴你哪一個是當前的做到這一點:

void all_iterator::increment() 
{ 
    switch (current_member) { 
    case BasesMember: 
     ++bases_iter; 
     if (bases_iter==bases.end()) { 
     current_member = DerivedAsMember; 
     } 
     return; 
    case DerivedAsMember: 
     ++derived_as_iter; 
     if (derived_as_iter==derivedAs.end()) { 
     current_member = DerivedBsMember; 
     } 
     return; 
    case DerivedBsMember: 
     ++derived_bs_iter; 
     if (derived_bs_iter==derivedBs.end()) { 
     current_member = EndMember; 
     } 
     return; 
    case EndMember: 
     assert(current_member!=EndMember); 
     break; 
    } 
} 

bool all_iterator::equal(all_iterator const &other) const 
{ 
    if (current_member!=other.current_member) return false; 
    switch (current_member) { 
    case BasesMember: 
     return bases_iter==other.bases_iter; 
     break; 
    case DerivedAsMember: 
     return derived_as_iter==other.derived_as_iter; 
     break; 
    case DerivedBsMember: 
     return derived_bs_iter==other.derived_bs_iter; 
     break; 
    case EndMember: 
     return true 
    } 
} 

Base& all_iterator::dereference() const 
{ 
    switch (current_member) { 
    case BasesMember:  return *bases_iter; 
    case DerivedAsMember: return *derived_as_iter; 
    case DerivedBsMember: return *derived_bs_iter; 
    case EndMember: 
     assert(current_member!=EndMember); 
     break; 
    } 
    return *bases_iter; 
} 
+0

如果向量是Base類型或從base派生的類型,我不能在比較中投入基礎嗎? – gnzlbg 2012-07-23 14:26:02

+0

不,你實際上需要三個單獨的迭代器。 – 2012-07-23 14:29:51

2

首先我們應該注意到,你的代碼是未定義的行爲,如果bases是空的,或derivedBs任何尺寸,如果end_begin被調用。

是否有一個原因,你不能說有BaseType*或智能變種在一個容器中,並使用抽象接口來訪問它,而不是說dynamic_cast/static_cast鏈較爲明顯和正常的方法呢?那麼問題就完全消失了。

編輯:如果你需要在每個類型的內存是連續出於某種原因,你不要做單insert S插入容器頻繁,剛剛創建的BaseType指針指向每個對象中派生的容器對象容器。但是我會要求你稍微回顧並回顧一下爲什麼你需要這些物體是連續的(這很可能是一個合法的原因)。

+1

如果存在這樣一個容器,'boost :: indirect_iterator'(來自Boost.Iterators)風格的東西可以很容易地實現'all_begin' /'all_end'。 – 2012-07-23 14:27:57

+0

唯一的原因是我需要向量中的元素在內存中對齊。不知道是否有更好的方法。 – gnzlbg 2012-07-23 14:32:34

+0

謝謝! BaseType指針容器的想法很好! – gnzlbg 2012-07-23 14:47:42

2

我假設BaseType是DerivedA和DerivedB的公共基礎,並且您希望有一個容器包含DerivedA和DerivedB的實例,併爲您提供遍歷所有DerivedA實例,覆蓋所有DerivedB實例和覆蓋BaseType的所有實例(即DerivedA's和DerivedB's的聯合)。你可以這樣做:

class BaseType 
{ 
public: 
    virtual void doit() const = 0; 

    virtual ~BaseType() { } 
}; 

class DerivedA : public BaseType 
{ 
public: 
    void doit() const { std::cout << "DerivedA::doit()" << std::endl; } 

    void a() const { std::cout << "DerivedA::a()" << std::endl; } 
}; 

class DerivedB : public BaseType 
{ 
public: 
    void doit() const { std::cout << "DerivedB::doit()" << std::endl; } 

    void b() const { std::cout << "DerivedB::b()" << std::endl; } 
}; 

class Container 
{ 
public: 
    void insert(DerivedA const & a) 
    { 
    m_as.push_back(a); 
    m_base.push_back(&m_as.back()); 
    } 

    void insert(DerivedB const & b) 
    { 
    m_bs.push_back(b); 
    m_base.push_back(&m_bs.back()); 
    } 

    std::vector<DerivedA>::iterator begin_a() { return m_as.begin(); } 
    std::vector<DerivedA>::iterator end_a() { return m_as.end(); } 
    std::vector<DerivedB>::iterator begin_b() { return m_bs.begin(); } 
    std::vector<DerivedB>::iterator end_b() { return m_bs.end(); } 
    std::vector<BaseType *>::iterator begin_all() { return m_base.begin(); } 
    std::vector<BaseType *>::iterator end_all() { return m_base.end(); } 

protected: 
private: 
    std::vector<DerivedA> m_as; 
    std::vector<DerivedB> m_bs; 
    std::vector<BaseType *> m_base; 
}; 
+0

謝謝!這是一個不錯的選擇。我需要檢查具有BaseType指針向量的內存開銷:D – gnzlbg 2012-07-23 14:53:51