有没有更好的暴露std :: vector接口的方法?

时间:2014-01-01 18:51:35

标签: c++ c++11 vector stl

我正在编写一个std::vector包装器,它使用自定义分配器通过.dll与PascalScript解释器通信。下面的代码工作正常,但更新和编写并破坏我的眼睛是相当繁琐的。

此解释器中的数组以&Array_Ptr[0] - sizeof(int)的大小连续存储。

想要使用std::vector for这些数组,我决定编写一个自定义分配器,它分配size + sizeof(int)并让向量包装器将大小设置为ptr[0]。因此,数据存储在&ptr[0] + sizeof(int)

问题是,如果我想使用矢量接口,我必须手动写出所有std::vector's函数,因为从它继承是不好的吗?

我想出了以下代码:

template<typename T>
class PascalAllocator : public BasicAllocator<T> //BasicAllocator is equivalent to std::allocator with minor changes.
{
    public:
        typedef typename BasicAllocator<T>::pointer pointer;
        typedef typename BasicAllocator<T>::size_type size_type;
        typedef typename BasicAllocator<T>::value_type value_type;

        template<typename U>
        struct rebind {typedef PascalAllocator<U> other;};

        pointer allocate(size_type n, const void* hint = 0)
        {
            std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(::operator new((n * sizeof(value_type)) + sizeof(std::int32_t)));
            return reinterpret_cast<pointer>(++data_ptr);
        }

        void deallocate(void* ptr, size_type n)
        {
            if (ptr)
            {
                std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(ptr);
                ::operator delete(reinterpret_cast<T*>(--data_ptr));
            }
        }
};

template<typename T, typename Allocator = PascalAllocator<T>>
class PascalVector
{
    private:
        std::vector<T, Allocator> Data;
        inline std::int32_t* size_ptr() {return reinterpret_cast<std::int32_t*>(&Data[0]) - 1;}
        inline const std::int32_t* size_ptr() const {return reinterpret_cast<std::int32_t*>(&Data[0]) - 1;}

    public:
        typedef std::size_t                                          size_type;
        typedef std::ptrdiff_t                                       difference_type;
        typedef T*                                                   pointer;
        typedef const T*                                             const_pointer;
        typedef T&                                                   reference;
        typedef const T&                                             const_reference;
        typedef T                                                    value_type;
        typedef typename std::vector<T, Allocator>::iterator         iterator;
        typedef typename std::vector<T, Allocator>::const_iterator   const_iterator;
        typedef std::reverse_iterator<const_iterator>                const_reverse_iterator;
        typedef std::reverse_iterator<iterator>                      reverse_iterator;

        explicit PascalVector(const Allocator& alloc = Allocator()) : Data(std::forward<decltype(alloc)>(alloc)) {*size_ptr() = 0;}
        explicit PascalVector(size_type size, const Allocator& alloc = Allocator()) : Data(size, std::forward<decltype(alloc)>(alloc)) {*size_ptr() = size - 1;}
        explicit PascalVector(size_type size, const T &value, const Allocator& alloc = Allocator()) : Data(size, std::forward<decltype(value)>(value), std::forward<decltype(alloc)>(alloc)) {*size_ptr() = size - 1;}

        template<class InputIt>
        PascalVector(InputIt first, InputIt second, const Allocator &alloc = Allocator()) : Data(first, second, std::forward<decltype(alloc)>(alloc)) {*size_ptr() = Data.size() - 1;}

        PascalVector(const PascalVector &other) : Data(other.Data) {}
        PascalVector(const PascalVector &other, const Allocator& alloc) : Data(other.Data, std::forward<decltype(alloc)>(alloc)) {}
        PascalVector(PascalVector && other) : Data(std::move(other.Data)) {}
        PascalVector(PascalVector && other, const Allocator& alloc) : Data(std::move(other.Data), std::move(alloc)) {}
        PascalVector(const std::initializer_list<T> &init, const Allocator& alloc = Allocator()) : Data(init, alloc) {}

        inline PascalVector& operator = (PascalVector other) {Data.operator = (std::forward<decltype(other.Data)>(other.Data)); return *this;}
        inline PascalVector& operator = (std::initializer_list<T> ilist) {Data.operator = (std::forward<decltype(ilist)>(ilist)); return *this;}
        inline PascalVector& operator = (PascalVector && other) {Data.operator = (std::forward<decltype(other.Data)>(other.Data)); return *this;}

        template<class InputIt>
        inline void assign(InputIt first, InputIt second) {Data.assign(first, second);};
        inline void assign(size_type count, const T& value) {Data.assign(count, std::forward<decltype(value)>(value));}
        inline void assign(std::initializer_list<T> ilist) {Data.assign(std::forward<decltype(ilist)>(ilist));}
        inline Allocator get_allocator() const {return Data.get_allocator();}

        inline reference at(size_type pos) {return Data.at(pos);}
        inline const_reference at(size_type pos) const {return Data.at(pos);}
        inline reference operator[](size_type pos) {return Data[pos];}
        inline const_reference operator[](size_type pos) const {return Data[pos];}
        inline reference front() {return Data.front();}
        inline constexpr const_reference front() const {return Data.front();}
        inline reference back() {return Data.back();}
        inline constexpr const_reference back() const {return Data.back();}
        inline pointer data() {return Data.data();}
        inline const_pointer data() const {return Data.data();}

        inline iterator begin() {return Data.begin();}
        inline const_iterator begin() const {return Data.begin();}
        inline const_iterator cbegin() const {return Data.cbegin();}
        inline iterator end() {return Data.end();}
        inline const_iterator end() const {return Data.end();}
        inline const_iterator cend() const {return Data.cend();}
        inline reverse_iterator rbegin() {return Data.rbegin();}
        inline const_reverse_iterator rbegin() const {return Data.rbegin();}
        inline const_reverse_iterator crbegin() const {return Data.rbegin();}
        inline reverse_iterator rend() {return Data.rend();}
        inline const_reverse_iterator rend() const {return Data.rend();}
        inline const_reverse_iterator crend() const {return Data.crend();}

        inline bool empty() const {return Data.empty();}
        inline size_type size() const {return Data.size();}
        inline size_type max_size() const {return Data.max_size();}
        inline void reserve(size_type new_cap) {Data.reserve(size);}
        inline size_type capacity() const {return Data.capacity();}
        inline void shrink_to_fit() {Data.shrink_to_fit(); *size_ptr() = Data.size() - 1;}

        inline void clear() {Data.clear(); *size_ptr() = 0;}

        inline iterator insert(iterator pos, const T& value) {return Data.insert(pos, std::forward<decltype(value)>(value));  *size_ptr() = Data.size() - 1;}
        inline void insert(iterator pos, size_type count, const T& value) {Data.insert(pos, count, std::forward<decltype(value)>(value));  *size_ptr() = Data.size() - 1;}
        template<class InputIt>
        inline void insert(iterator pos, InputIt first, InputIt last) {Data.insert(pos, first, last);  *size_ptr() = Data.size() - 1;}
        inline void insert(iterator pos, std::initializer_list<T> ilist) {Data.insert(pos, std::forward<decltype(ilist)>(ilist));  *size_ptr() = Data.size() - 1;}

        template<class... Args>
        inline iterator emplace(iterator pos, Args && ... args) {iterator res = Data.emplace(pos, std::forward<Args>(args)...);  *size_ptr() = Data.size() - 1; return res;}
        template<class... Args>
        inline void emplace_back(Args && ... args) {Data.emplace_back(std::forward<Args>(args)...);  *size_ptr() = Data.size() - 1;}

        inline iterator erase(iterator pos) {iterator res = Data.erase(pos); *size_ptr() = Data.size(); return res;}
        inline iterator erase(iterator first, iterator last) {iterator res = Data.erase(first, last);  *size_ptr() = Data.size() - 1; return res;}

        inline void push_back(const T& value) {Data.push_back(std::forward<decltype(value)>(value)); *size_ptr() = Data.size() - 1;}
        inline void push_back(T && value) {Data.push_back(std::forward<T>(value)); *size_ptr() = Data.size() - 1;}
        inline void pop_back() {Data.pop_back(); *size_ptr() = Data.size() - 1;}

        inline void resize(size_type count, T value = T()) {Data.resize(count, std::forward<decltype(value)>(value)); *size_ptr() = count - 1;}
        inline void swap(PascalVector& other) {Data.swap(other.Data);}
};

现在PascalAllocator很好。令我讨厌的是PascalVector界面。

有任何更简单的方法吗?



修改

根据我得到的一些回复,我尝试按照以下方式实现自己的向量:

template<typename T, typename Allocator = PascalAllocator<T>>
class PSArray : private Allocator
{
    private:
        typename Allocator::pointer first;
        typename Allocator::pointer last;
        typename Allocator::size_type _size;
        typename Allocator::pointer allocmem(typename Allocator::size_type n, const T& value);
        void deallocmem();

    public:
        typedef T                                                    value_type;
        typedef typename Allocator::pointer                          pointer;
        typedef typename Allocator::const_pointer                    const_pointer;
        typedef typename Allocator::reference                        reference;
        typedef typename Allocator::const_reference                  const_reference;
        typedef typename Allocator::size_type                        size_type;
        typedef typename Allocator::difference_type                  difference_type;
        typedef typename Allocator::pointer                          iterator;
        typedef typename Allocator::const_pointer                    const_iterator;
        typedef std::reverse_iterator<const_iterator>                const_reverse_iterator;
        typedef std::reverse_iterator<iterator>                      reverse_iterator;
        typedef Allocator                                            allocator_type;

        allocator_type get_allocator() const {return static_cast<const Allocator&>(*this);}

        iterator begin()             {return first;}
        iterator end()               {return last;}
        const_iterator begin() const {return first;}
        const_iterator end() const   {return last;}
        size_type size() const {return _size;}

        PSArray(size_type n = 0, const T& value = T(), const Allocator& alloc = Allocator());
        PSArray(const PSArray &other);
        PSArray(PSArray&& other);
        ~PSArray();

        PSArray& operator = (const PSArray &other);
        inline reference operator[](size_type pos) {return first[pos];}
        inline const_reference operator[](size_type pos) const {return first[pos];}
};

template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(size_type n, const T& value, const Allocator& alloc) : Allocator(alloc), first(0), last(0), _size(0)
{
    this->first = this->allocmem(n, value);
    this->last = &first[0] + n + 1;
    reinterpret_cast<std::int32_t*>(first)[-1] = n - 1; //*(reinterpret_cast<std::int32_t*>(&first[0]) - 1) = n - 1;
    _size = n;
}

template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(const PSArray &other) : Allocator(other.get_allocator()), first(0), last(0), _size(other._size)
{
    this->first = Allocator::allocate(other._size);
    this->last = &first[0] + _size + 1;
    memcpy(&first[0], &other.first[0], other._size * sizeof(T));
    reinterpret_cast<std::int32_t*>(first)[-1] = _size - 1; //*(reinterpret_cast<std::int32_t*>(&first[0]) - 1) = _size - 1;
}

template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(PSArray&& other) : first(other.first), last(other.last), _size(other._size)
{
    other.first = nullptr;
    other.last = nullptr;
    other._size = 0;
}

template <class T, class Allocator>
PSArray<T, Allocator>::~PSArray()
{
    this->deallocmem();
}

template <class T, class Allocator>
PSArray<T, Allocator>& PSArray<T, Allocator>::operator = (const PSArray &other)
{
    _size = other._size;
    this->first = Allocator::allocate(other._size);
    this->last = &first[0] + _size + 1;
    memcpy(&first[0], &other.first[0], other._size * sizeof(T));
    reinterpret_cast<std::int32_t*>(first)[-1] = _size - 1;
    return *this;
}

template <class T, class Allocator>
typename Allocator::pointer PSArray<T, Allocator>::allocmem(typename Allocator::size_type n, const T& value)
{
    if (n != 0)
    {
        size_type i = 0;
        typename Allocator::pointer res = Allocator::allocate(n);

        try
        {
            for (i = 0; i < n; ++i)
            {
                Allocator::construct(res + i, value);
            }
        }
        catch(...)
        {
            for(size_type j = 0; j < i; ++j)
            {
                Allocator::destroy(res + j);
            }
            Allocator::deallocate(res, n);
            throw;
        }
        return res;
    }
    return nullptr;
}

template <class T, class Allocator>
void PSArray<T, Allocator>::deallocmem()
{
    if (first != last)
    {
        for (iterator i = first; i < last; ++i)
        {
            Allocator::destroy(i);
        }
        Allocator::deallocate(first, last - first);
    }
}

到目前为止工作得非常好。尽管如此,以前的代码还有很多工作要做。

1 个答案:

答案 0 :(得分:5)

PascalVector看起来像std::vector看起来没有比实现std::vector的整个界面更简单的方法了。

  • 您不应公开继承std::vector,因为std::vector没有虚拟析构函数,这意味着删除指向std::vector的指针不会调用PascalVector的析构函数即使指针指向PascalVector
  • 您不应私下继承std::vector,因为std::vector中缺少虚函数,您将针对实现进行编程,而不是接口。
  • 使用std::vector的{​​{1}}的typedef是不够的,因为PascalAllocator没有被告知向量大小的变化(因此无法将大小写入正确位置)。

BTW:类中定义的函数自动被视为PascalAllocator。在这些情况下,inline关键字是多余的。