【问题标题】:Writing your own STL Container编写自己的 STL 容器
【发布时间】:2011-12-07 04:46:06
【问题描述】:

是否有关于如何编写与任何STL 容器一样的新容器的指南?

【问题讨论】:

  • 查看现有标准容器的实现,并尝试理解它们 - 函数、返回类型、运算符重载、嵌套类型、内存管理等等。
  • 我通常首先从 msdn 或标准复制概念上最接近我正在做的任何容器的成员函数原型。 (cplusplus.com没有C++11函数,和www.sgi.com不匹配)
  • @Mooing Duck:你觉得msdn比sgi更接近标准?
  • 绝对是。 MSDN 是最新的 - SGI 是 pre-Standard
  • 迄今为止最好的在线参考资料(完整性、正确性,尤其是可用性)是 cppreference.com。它还解释了除了库之外的大量语言功能。而且它是一个 wiki,所以它应该比 cplusplus.com 包含更少的错误。

标签: c++ stl c++-standard-library


【解决方案1】:

这是我从第 23.2.1\4 节拼凑而成的序列伪容器。注意iterator_category 应该是std::input_iterator_tagstd::output_iterator_tagstd::forward_iterator_tagstd::bidirectional_iterator_tagstd::random_access_iterator_tag 之一。另请注意,以下技术上比要求的更严格,但这就是想法。请注意,由于迭代器的强大功能,绝大多数“标准”函数在技术上都是可选的。

template <class T, class A = std::allocator<T> >
class X {
public:
    typedef A allocator_type;
    typedef typename A::value_type value_type; 
    typedef typename A::reference reference;
    typedef typename A::const_reference const_reference;
    typedef typename A::difference_type difference_type;
    typedef typename A::size_type size_type;

    class iterator { 
    public:
        typedef typename A::difference_type difference_type;
        typedef typename A::value_type value_type;
        typedef typename A::reference reference;
        typedef typename A::pointer pointer;
        typedef std::random_access_iterator_tag iterator_category; //or another tag

        iterator();
        iterator(const iterator&);
        ~iterator();

        iterator& operator=(const iterator&);
        bool operator==(const iterator&) const;
        bool operator!=(const iterator&) const;
        bool operator<(const iterator&) const; //optional
        bool operator>(const iterator&) const; //optional
        bool operator<=(const iterator&) const; //optional
        bool operator>=(const iterator&) const; //optional

        iterator& operator++();
        iterator operator++(int); //optional
        iterator& operator--(); //optional
        iterator operator--(int); //optional
        iterator& operator+=(size_type); //optional
        iterator operator+(size_type) const; //optional
        friend iterator operator+(size_type, const iterator&); //optional
        iterator& operator-=(size_type); //optional            
        iterator operator-(size_type) const; //optional
        difference_type operator-(iterator) const; //optional

        reference operator*() const;
        pointer operator->() const;
        reference operator[](size_type) const; //optional
    };
    class const_iterator {
    public:
        typedef typename A::difference_type difference_type;
        typedef typename A::value_type value_type;
        typedef typename const A::reference reference;
        typedef typename const A::pointer pointer;
        typedef std::random_access_iterator_tag iterator_category; //or another tag

        const_iterator ();
        const_iterator (const const_iterator&);
        const_iterator (const iterator&);
        ~const_iterator();

        const_iterator& operator=(const const_iterator&);
        bool operator==(const const_iterator&) const;
        bool operator!=(const const_iterator&) const;
        bool operator<(const const_iterator&) const; //optional
        bool operator>(const const_iterator&) const; //optional
        bool operator<=(const const_iterator&) const; //optional
        bool operator>=(const const_iterator&) const; //optional

        const_iterator& operator++();
        const_iterator operator++(int); //optional
        const_iterator& operator--(); //optional
        const_iterator operator--(int); //optional
        const_iterator& operator+=(size_type); //optional
        const_iterator operator+(size_type) const; //optional
        friend const_iterator operator+(size_type, const const_iterator&); //optional
        const_iterator& operator-=(size_type); //optional            
        const_iterator operator-(size_type) const; //optional
        difference_type operator-(const_iterator) const; //optional

        reference operator*() const;
        pointer operator->() const;
        reference operator[](size_type) const; //optional
    };

    typedef std::reverse_iterator<iterator> reverse_iterator; //optional
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator; //optional

    X();
    X(const X&);
    ~X();

    X& operator=(const X&);
    bool operator==(const X&) const;
    bool operator!=(const X&) const;
    bool operator<(const X&) const; //optional
    bool operator>(const X&) const; //optional
    bool operator<=(const X&) const; //optional
    bool operator>=(const X&) const; //optional

    iterator begin();
    const_iterator begin() const;
    const_iterator cbegin() const;
    iterator end();
    const_iterator end() const;
    const_iterator cend() const;
    reverse_iterator rbegin(); //optional
    const_reverse_iterator rbegin() const; //optional
    const_reverse_iterator crbegin() const; //optional
    reverse_iterator rend(); //optional
    const_reverse_iterator rend() const; //optional
    const_reverse_iterator crend() const; //optional

    reference front(); //optional
    const_reference front() const; //optional
    reference back(); //optional
    const_reference back() const; //optional
    template<class ...Args>
    void emplace_front(Args&&...); //optional
    template<class ...Args>
    void emplace_back(Args&&...); //optional
    void push_front(const T&); //optional
    void push_front(T&&); //optional
    void push_back(const T&); //optional
    void push_back(T&&); //optional
    void pop_front(); //optional
    void pop_back(); //optional
    reference operator[](size_type); //optional
    const_reference operator[](size_type) const; //optional
    reference at(size_type); //optional
    const_reference at(size_type) const; //optional

    template<class ...Args>
    iterator emplace(const_iterator, Args&&...); //optional
    iterator insert(const_iterator, const T&); //optional
    iterator insert(const_iterator, T&&); //optional
    iterator insert(const_iterator, size_type, T&); //optional
    template<class iter>
    iterator insert(const_iterator, iter, iter); //optional
    iterator insert(const_iterator, std::initializer_list<T>); //optional
    iterator erase(const_iterator); //optional
    iterator erase(const_iterator, const_iterator); //optional
    void clear(); //optional
    template<class iter>
    void assign(iter, iter); //optional
    void assign(std::initializer_list<T>); //optional
    void assign(size_type, const T&); //optional

    void swap(X&);
    size_type size() const;
    size_type max_size() const;
    bool empty() const;

    A get_allocator() const; //optional
};
template <class T, class A = std::allocator<T> >
void swap(X<T,A>&, X<T,A>&); //optional

另外,每当我制作一个容器时,我都会用一个或多或少这样的类进行测试:

#include <cassert>
struct verify;
class tester {
    friend verify;
    static int livecount;
    const tester* self;
public:
    tester() :self(this) {++livecount;}
    tester(const tester&) :self(this) {++livecount;}
    ~tester() {assert(self==this);--livecount;}
    tester& operator=(const tester& b) {
        assert(self==this && b.self == &b);
        return *this;
    }
    void cfunction() const {assert(self==this);}
    void mfunction() {assert(self==this);}
};
int tester::livecount=0;
struct verify {
    ~verify() {assert(tester::livecount==0);}
}verifier;

制作tester 对象的容器,并在测试容器时调用每个对象的function()。不要创建任何全局 tester 对象。如果你的容器在任何地方作弊,这个 tester 类将 assert 并且你会知道你不小心在某个地方作弊。

【讨论】:

  • 这很有趣。你的测试仪是如何工作的?有几个解析错误,这些错误是微不足道的(缺少';'),但不确定验证析构函数是如何工作的。哦,你的意思是assert(tester::livecount == 0);。嗯,仍然不确定这个测试器框架是如何工作的。能举个例子吗?
  • 测试者有一个指向自身的非静态成员,析构函数和成员是一种检查无效memcpy的方法。 (测试不是万无一失的,但它抓住了一些)。 livecount 是一个简单的泄漏检测器,可确保您的容器调用相同数量的构造函数和析构函数。
  • 好的,我明白了,但这如何测试您的迭代器?顺便说一句,我认为您的意思是 verifier 不是 varifier
  • @Adrian 不不,你写你的容器,然后把一堆这些放在容器里,用容器做事,验证你没有不小心memcpy,记得调用所有的析构函数。
  • 我可以建议从标题std::iterator 继承迭代器&lt;iterator&gt;
【解决方案2】:

这是一个非常简单的伪向量实现,它基本上是std::vector 的包装器,并且有自己的(但真实的)迭代器,它模仿了 STL 迭代器。同样,迭代器非常简单,跳过了许多概念,如const_iterator、有效性检查等。

代码开箱即用。

#include <iostream>
#include <string>
#include <vector>

template<typename T>
struct It
{
    std::vector<T>& vec_;
    int pointer_;

    It(std::vector<T>& vec) : vec_{vec}, pointer_{0} {}

    It(std::vector<T>& vec, int size) : vec_{vec}, pointer_{size} {}

    bool operator!=(const It<T>& other) const
    {
        return !(*this == other);
    }

    bool operator==(const It<T>& other) const
    {
        return pointer_ == other.pointer_;
    }

    It& operator++()
    {
        ++pointer_;            
        return *this;
    }

    T& operator*() const
    {
        return vec_.at(pointer_);   
    }
};

template<typename T>
struct Vector
{
    std::vector<T> vec_;

    void push_back(T item)
    {
        vec_.push_back(item);
    };

    It<T> begin()
    {
        return It<T>(vec_);
    }

    It<T> end()
    {
        return It<T>(vec_, vec_.size());
    }
};

int main()
{
  Vector<int> vec;
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);

  bool first = true;
  for (It<int> it = vec.begin(); it != vec.end(); ++it)
  {
      if (first) //modify container once while iterating
      {
          vec.push_back(4);
          first = false;
      }

      std::cout << *it << '\n'; //print it 
      (*it)++;                  //change it
  }

  for (It<int> it = vec.begin(); it != vec.end(); ++it)
  {
      std::cout << *it << '\n'; //should see changed value
  }
}

【讨论】:

    【解决方案3】:

    您需要阅读有关容器的 C++ 标准部分以及 C++ 标准对容器实现的要求。

    C++03标准相关章节为:

    第 23.1 节容器要求

    C++11标准相关章节为:

    第 23.2 节容器要求

    C++11 标准的接近终稿可免费获得 here

    您不妨阅读一些优秀的书籍,这些书籍将帮助您从容器用户的角度了解需求。两本很容易打动我的好书是:

    Effective STL by Scott Meyers &
    The C++ Standard Library: A Tutorial and Reference > 尼古拉·乔苏蒂尔斯

    【讨论】:

      猜你喜欢
      • 2010-09-28
      • 1970-01-01
      • 2011-09-12
      • 2011-02-09
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多