【问题标题】:Give access to encapsulated container允许访问封装容器
【发布时间】:2011-10-13 04:34:21
【问题描述】:
class X {
  public:
    typedef std::list<int> Container;

    // (1)
    const Container& GetElements() const;

    // (2)
    Container::iterator ElementBegin();
    Container::iterator ElementEnd();

    // (3)
    CustomIterator GetElementIterator();

  private:
    Container m_container;
};

我正在寻找一种一致且干净的方式来为调用者提供封装容器的迭代器。我想出了上面源代码中标记的三个想法。

  1. 提供 size()、begin() 和 end(),所有这些都非常适合读取访问。但是,由于返回的 Container 引用是 const,您将只能使用 const_iterator。返回引用非常量是不好的,因为容器本身可以被修改(例如clear())。
  2. 提供对元素的非常量访问,但是我们通常需要一个自己的size() 方法(如GetElementCount())。可以使用iterator::distance(),但对于某些容器来说可能效率低下(其中operator++/-- 被反复调用以计算距离)。
  3. 提供带有 next() 等方法的自定义迭代器。仍然需要自己的 size() 方法。

我敢打赌有更好的解决方案,所以如果你知道的话,我会很高兴看到它们。

【问题讨论】:

  • 如果你想从容器中暴露几乎任何东西,为什么不继承它,而不是封装它呢?
  • @Sander De Dyker:因为我不想以任何方式扩展容器(无论如何,STL containere 并不意味着派生)。甚至有可能在一个类中存在多个容器,例如 X 之一。
  • 在一般情况下确实不是一个好主意。但在特定情况下,它可能很有用(假设您知道自己在做什么)。这似乎是一个有争议的问题,因为它不适用于这里。

标签: c++ stl iterator


【解决方案1】:

混合使用 (2) 和 (3) 可能就是我要做的:

class X {
  public :
    typedef std::list<int> ElementContainer;
    typedef ElementContainer::size_type ElementSizeType;
    typedef ElementContainer::iterator ElementIterator;
    typedef ElementContainer::const_iterator ConstElementIterator;

    ElementIterator elementBegin() { return m_container.begin(); }
    ElementIterator elementEnd() { return m_container.end(); }

    ConstElementIterator elementBegin() const { return m_container.begin(); }
    ConstElementIterator elementEnd() const { return m_container.end(); }

    ElementSizeType elementSize() const { return m_container.size(); }

  private :
    ElementContainer m_container;
};

它仍然有编写自定义迭代器的空间(通过更改typedefs),但只要容器提供的那些没问题,它们就可以使用。

【讨论】:

  • 这对我来说似乎是一个很好的妥协。写的有点多,但可以替换。谢谢!
【解决方案2】:

我会改用这些名称:iteratorconst_iteratorbeginendcbegincendsize()

class X 
{
  public :

    typedef std::list<int>::iterator iterator;
    typedef std::list<int>::const_iterator const_iterator ;

    iterator begin() { return m_container.begin(); }
    iterator end() { return m_container.end(); }

    const_iterator cbegin() const { return m_container.begin(); }
    const_iterator cend() const { return m_container.end(); }

    size_t size() const { return m_container.size(); }

  private :
    std::list<int> m_container;
};

如果你可以使用C++0x,那么使用m_container.cbegin()m_container.cend()作为:

const_iterator cbegin() const { return m_container.cbegin(); }
const_iterator cend() const { return m_container.cend(); }

【讨论】:

  • 我更喜欢使用方法名作为 begin() 而不是 ElementBegin()。这让我们觉得类是一个容器。
【解决方案3】:

我想不出更清洁的方法;您可以考虑使用轻量级 (4) 解决方案提供访问权限

const Container& container() const { return m_container; }

我更喜欢 (3),因为容器类型已完全封装,即您的类型不一定需要包含,并且您可以更改容器类型而无需重新编译依赖模块。

【讨论】:

  • 您的第 4 个提案正是我的第一个提案,但它只能访问 const 元素。第三种解决方案也是我最初的想法,但我仍然想知道我是否不能使用 STL 迭代器来完成它,因为它们已经在那里了。 :-)
  • 当然可以,但请记住它不仅是 begin 和 end,还有 rbegin、rend 和 reverse_iterator。很快就会变得很乱。
【解决方案4】:

当然,最简单的是这个:

class X {
  public:
    typedef std::list<int> Container;

    Container m_container;
};

但这会让你的 class X 过时。

除此之外,如果你真的喜欢你的课程,那么添加下一个方法:

Container::const_iterator ElementBegin() const;
Container::const_iterator ElementEnd() const;
int size() const;

【讨论】:

  • 当然这个类不仅仅包含容器和访问方法,那完全没用。可能有加载例程或其他有用的方法在容器上运行。
  • @Tank 这不是你在问题中写的。
  • 抱歉,不清楚。但显然一个只拥有一个容器并且只允许访问它的类是没有用的。 ;-) 对不起。
【解决方案5】:

2 和 3 确实不是不同的选项。但是,所写的 3 几乎没用。没有 STL 算法将使用CustomIterator::next。为了 STL 兼容性,你会写:

// mix of 2 and 3
CustomIterator begin();
CustomIterator end();

并给CustomIterator标准operator++operator*

【讨论】:

    【解决方案6】:

    如果你可以使用 Boost,有一个库适合你:http://www.boost.org/doc/libs/1_47_0/libs/iterator/doc/index.html 具体看iterator_facade和iterator_adapter

    这里是一个示例 - 它提供了一个 std::vector 持有者,以及与 STL 兼容的迭代器。您可以通过添加其他方法来扩展它,例如 operator[]、size()、push_back() 等。

    模板 类 VectorHolder { 民众: typedef T value_type;

    公开: 矢量持有人() : m_values() { }

    公开: typedef typename std::vector::iterator vector_iterator; typedef typename std::vector::const_iterator vector_const_iterator;

    class iterator : public boost::iterator_adaptor<iterator, vector_iterator>
    {
    public:
        iterator()
            : iterator::iterator_adaptor_()
        {
        }
    
        iterator(const vector_iterator& it)
            : iterator::iterator_adaptor_(it)
        {
        }
    
    private:
        friend class boost::iterator_core_access;
    };
    
    class const_iterator : public boost::iterator_adaptor<const_iterator, vector_const_iterator>
    {
    public:
        const_iterator()
            : const_iterator::iterator_adaptor_()
        {
        }
    
        const_iterator(const vector_const_iterator& it)
            : const_iterator::iterator_adaptor_(it)
        {
        }
    
        const_iterator(const iterator& it)
            : const_iterator::iterator_adaptor_(it.base())
        {
        }
    private:
        friend class boost::iterator_core_access;
    };
    
    
    iterator begin()
    {
        return iterator(m_values.begin());
    }
    
    iterator end()
    {
        return iterator(m_values.end());
    }
    
    const_iterator begin() const
    {
        return const_iterator(m_values.begin());
    }
    
    const_iterator end() const
    {
        return const_iterator(m_values.end());
    }protected:
    std::vector<T> m_values;};
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多