C++ 在Base类型和/或从Base派生的类型的向量上迭代
我需要存储Base类型的对象,以及BaseDerivedA和BaseDerivedB派生类型的对象。这些对象需要在内存中对齐。我想提供一个迭代所有对象的迭代器。我希望避免存储基指针向量的内存开销 为此,我构建了以下容器C++ 在Base类型和/或从Base派生的类型的向量上迭代,c++,c++11,iterator,C++,C++11,Iterator,我需要存储Base类型的对象,以及BaseDerivedA和BaseDerivedB派生类型的对象。这些对象需要在内存中对齐。我想提供一个迭代所有对象的迭代器。我希望避免存储基指针向量的内存开销 为此,我构建了以下容器 struct Container { std::vector<Base> bases; std::vector<BaseDerivedA> derivedAs; std::vector<BaseDerivedB> der
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作为结束迭代器之后的一个迭代器,并且使用了很多cast。我还向迭代器传递一个指向数据结构的指针
是否有更好的方法来迭代包含类型Base或从Base派生的类型的三个向量?为什么位于derivedAs.end的内容很重要?您永远不会通过derivedAs访问/修改它。所以你根本不需要这个假设 典型的代码是
for(auto it = derivedAs.begin(); it != derivedAs.end(); ++it) {
*it = // do whatever, will never do *derivedAs.end()
}
为什么位于derivedAs.end的位置很重要?您永远不会通过derivedAs访问/修改它。所以你根本不需要这个假设 典型的代码是
for(auto it = derivedAs.begin(); it != derivedAs.end(); ++it) {
*it = // do whatever, will never do *derivedAs.end()
}
为了使迭代器正确,您必须知道当前正在遍历的向量,以便能够正确地进行比较。您可以通过一个枚举来完成此操作,该枚举告诉您哪一个是当前的:
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;
}
为了使迭代器正确,您必须知道当前正在遍历的向量,以便能够正确地进行比较。您可以通过一个枚举来完成此操作,该枚举告诉您哪一个是当前的:
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;
}
首先,我们应该注意,如果基是空的,则代码具有未定义的行为;如果调用end_begin,则对于任何大小的derivedBs,则代码具有未定义的行为 有没有一个原因让你无法理解,在一个容器中使用BaseType*或智能变体,并使用抽象接口来访问它,而不是动态/静态的转换链,这是一种更明显、更正常的方法?然后问题就完全消失了
编辑:如果出于某种原因需要每个类型的内存都是连续的,并且不经常向容器中进行单次插入,那么只需创建一个BaseType指针容器,指向派生对象容器中的每个对象。但是我想请您退后一步,回顾一下为什么需要对象是连续的,这很可能是一个合理的原因。首先,我们应该注意,如果基为空,您的代码具有未定义的行为;如果调用end\u begin,则对于任何大小的derivedBs,您的代码都具有未定义的行为 有没有一个原因让你无法理解,在一个容器中使用BaseType*或智能变体,并使用抽象接口来访问它,而不是动态/静态的转换链,这是一种更明显、更正常的方法?然后问题就完全消失了
编辑:如果出于某种原因需要每个类型的内存都是连续的,并且不经常向容器中进行单次插入,那么只需创建一个BaseType指针容器,指向派生对象容器中的每个对象。但是我想请您退一步,回顾一下为什么需要对象是连续的,这很容易是一个合理的原因。我假设BaseType是DerivedA和DerivedB的公共基,您希望有一个包含DerivedA和DerivedB实例的容器,并使您能够遍历所有对象DerivedA实例、所有DerivedB实例和所有BaseType实例,即DerivedA和DerivedB的并集。你可以这样做:
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;
};
我假设BaseType是DerivedA和DerivedB的公共基础,您希望有一个包含DerivedA和DerivedB实例的容器,并使您能够迭代所有DerivedA实例、所有DerivedB实例和所有BaseType实例,即DerivedA和DerivedB的并集。你可以这样做:
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;
};
您隐藏了大部分重要的代码,例如什么是全部迭代器?它是如何定义的?以及它如何使用它的参数。不管所有迭代器如何,您的代码的当前实现都表现出未定义的行为:您无法访问derivedBs[derivedBs.size],因为这是一个超出末尾的元素。我不能假设超过derivedBs末尾的元素不会是derivedAs的第一个元素。-为什么呢?考虑到不同容器的迭代器是不可比较的,这意味着什么呢?尽管如此,代码仍然表现出未定义的行为,这意味着所有的赌注都没有了。这种方法是不正确的,您应该尝试找到解决方案,而不是试图找到如何使未定义的行为起作用。@DavidRodríguez dribeas我添加了迭代器的定义:我不理解用例。BaseType是DerivedA和DerivedB的基类吗?是否还有另一个类是BaseType的公共基,DerivedA和DerivedB?您隐藏了大部分重要的代码,例如什么是all_iterator?它是如何定义的?以及它如何使用它的参数。代码的当前实现,而不考虑所有的迭代
或者表现出未定义的行为:您无法访问derivedBs[derivedBs.size],因为这是一个超出末尾的元素。我不能假设超过derivedBs末尾的元素不会是derivedBs的第一个元素。-为什么呢?考虑到不同容器的迭代器是不可比较的,这意味着什么呢?尽管如此,代码仍然表现出未定义的行为,这意味着所有的赌注都没有了。这种方法是不正确的,您应该尝试找到解决方案,而不是试图找到如何使未定义的行为起作用。@DavidRodríguez dribeas我添加了迭代器的定义:我不理解用例。BaseType是DerivedA和DerivedB的基类吗?是否还有另一个类是BaseType的公共基,DerivedA和DerivedB?位于derivedBs.end的是什么。如果derivedBs.end==derivedAs.begin循环中断。@gnzlbg比较从不同容器获得的迭代器是非法的。@ecatmur因此,我提供了自己的迭代器。位于derivedBs.end的内容很重要。如果derivedBs.end==derivedAs.begin循环中断。@gnzlbg比较从不同容器获得的迭代器是非法的。@出于这个原因,我提供了自己的迭代器。如果向量是Base类型或从Base派生的类型,我不能在比较中强制转换为Base吗?不,实际上,你需要三个独立的迭代器。如果向量是Base类型或从Base派生的类型,我能不能在比较中强制转换为Base?不,实际上,您将需要三个独立的迭代器。boost中的boost::indirect_迭代器样式的迭代器。如果有这样一个容器,迭代器将使实现all_begin/all_end变得非常容易。唯一的原因是我需要在内存中对齐向量的元素。不知道是否有更好的方法。谢谢!BaseType指针容器的想法很好!boost中的boost::indirect_iterator风格。如果有这样一个容器,迭代器将很容易实现所有_begin/all_end。唯一的原因是我需要在内存中对齐向量的元素。不知道是否有更好的方法。谢谢!BaseType指针容器的想法很好!谢谢这是一个不错的选择。我需要检查拥有一个基类型指针向量的内存开销:DThanks!这是一个不错的选择。我需要检查具有基类型指针向量的内存开销:D