【问题标题】:Can abstract container class and childs be iterable?抽象容器类和子类可以迭代吗?
【发布时间】:2018-02-06 08:00:21
【问题描述】:

我创建了一个具有以下基本功能的抽象容器类

class AbstractInventory
{
private:
    string name;
public:
    AbstracInventory(string name);
    virtual ~AbstractInventory();
    virtual size_t size() const = 0;
    virtual Stack* stackAt(size_t index) const = 0;
    virtual Stack* &stackAt(size_t index) = 0;
    Stack* operator[](int index) const { return(stackAt(index)); }
    Stack* &operator[](int index) { return(stackAt(index)); }
};

例如,这可以是派生类之一

class BasicInventory : public AbstractInventory
{
private:
    vector<Stack*> content;
public:
    BasicInventory(string name, int size, bool sorting = false, ItemFilter* f=nullptr);

    size_t size() const override { return(content.size()); }
    Stack* stackAt(size_t index) const override { return(content[index]);     }
    Stack*& stackAt(size_t index) override { return(content[index]); }
};

现在我的问题是是否有可能使抽象接口和它的子级可迭代像矢量或列表一样在 foreach 循环中使用它

【问题讨论】:

  • 实现 beginend 以返回迭代器
  • 比这复杂一点,基类需要一个专门的迭代器类,重载的operator++、+、distance、begin()、end()会调用容器的虚函数。您可能只能以这种方式安全地实现前向迭代器。
  • @MichaëlRoy 或者只使用指针(当然只有在可能的情况下):)
  • @MichaëlRoy 派生类使用什么(如果有的话)作为其容器并不重要。 AbstractInventory 要求通过索引访问元素,通过stackAt;任何派生类都别无选择,只能实现它。迭代器类只需要保存AbstractInventory 指针及其当前索引。
  • 为什么要从非常量方法返回对指针的引用?你打算做void stuff(AbstractInventory ai) { ai[10] = new Stack(); ... }之类的事情吗?

标签: c++ inheritance interface iterator


【解决方案1】:

您可以创建自己的迭代器,例如:

struct Stack {};

class AbstractInventory;

class AbstractInventoryIterator
{
public:
    using difference_type =  std::ptrdiff_t;
    using value_type = Stack*;
    using pointer = Stack**;
    using reference = Stack*&;
    using iterator_category = std::random_access_iterator_tag;

    AbstractInventoryIterator(AbstractInventory* inv, std::size_t index) : inv(inv), index(index) {}

    AbstractInventoryIterator(const AbstractInventoryIterator&) = default;
    AbstractInventoryIterator& operator =(const AbstractInventoryIterator&) = default;

    bool operator == (const AbstractInventoryIterator& rhs) const { return std::tie(inv, index) == std::tie(rhs.inv, rhs.index); }
    bool operator != (const AbstractInventoryIterator& rhs) const { return !(*this == rhs); }

    reference operator*() const;
    pointer operator->() const { return &operator*(); }

    AbstractInventoryIterator& operator ++() { ++index; return *this; }
    AbstractInventoryIterator& operator --() { --index; return *this; }

    AbstractInventoryIterator operator ++(int) { auto next(*this); ++*this; return next; }
    AbstractInventoryIterator operator --(int) { auto prev(*this); --*this; return prev; }

    AbstractInventoryIterator& operator += (difference_type n) { index += n; return *this; }
    AbstractInventoryIterator& operator -= (difference_type n) { index -= n; return *this; }

    AbstractInventoryIterator operator + (difference_type n) const { auto res(*this); res += n; return res; }
    AbstractInventoryIterator operator - (difference_type n) const { auto res(*this); res -= n; return res; }

    friend AbstractInventoryIterator operator + (difference_type n, AbstractInventoryIterator it) { return it + n; }
    difference_type operator - (const AbstractInventoryIterator& it) const { return index - it.index; }

    reference operator [](difference_type n) const;

    bool operator < (const AbstractInventoryIterator& rhs) const { return rhs - *this > 0; }
    bool operator > (const AbstractInventoryIterator& rhs) const { return rhs < *this; }
    bool operator <= (const AbstractInventoryIterator& rhs) const { return !(rhs < *this); }
    bool operator >= (const AbstractInventoryIterator& rhs) const { return !(*this < rhs); }

private:
    AbstractInventory* inv = nullptr;
    int index = 0;
};

class AbstractInventory
{
private:
    std::string name;
public:
    AbstractInventory(std::string name) : name(name) {}
    virtual ~AbstractInventory() = default;
    virtual std::size_t size() const = 0;
    virtual Stack* stackAt(std::size_t index) const = 0;
    virtual Stack* &stackAt(std::size_t index) = 0;
    Stack* operator[](int index) const { return(stackAt(index)); }
    Stack* &operator[](int index) { return(stackAt(index)); }

    AbstractInventoryIterator begin() { return {this, 0}; }
    AbstractInventoryIterator end() { return {this, size()};}
};


auto AbstractInventoryIterator::operator*() const
-> AbstractInventoryIterator::reference 
{ return inv->stackAt(index); }

auto AbstractInventoryIterator::operator [](difference_type n) const
-> AbstractInventoryIterator::reference
{ return inv->stackAt(index + n); }

相当于const_iterator

【讨论】:

  • 对我来说很好,但我有一个使用问题:如果我在库存中并且我想在带有 (c++11) foreach 的 Inventory 类方法中循环遍历它,如何我要处理这个引用吗?喜欢foreach(Stack* s : this) {...}
  • 应该是for (Stack*&amp; s : *this)
猜你喜欢
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多