如何在C ++模板容器中实现erase()方法

时间:2015-05-09 18:47:44

标签: c++ memory vector

对于书籍练习,我需要创建一个名为Vec的简单C ++容器(模仿std :: vector)。但是,我在实现一个简单的erase()方法时遇到了问题。我的目的是销毁该索引中的对象,然后将索引一个点后面的所有元素向后移回列表。

PS:我是C ++的新手,并且对C ++中的内存管理知之甚少。

Vec.h

#pragma once
#ifndef GUARD_VEC_H
#define GUARD_VEC_H

#define WARNING D_SCL_SECURE_NO_WARNINGS
#define WARNING_ID 4996

#pragma message (WARNING)
#pragma warning (disable: WARNING_ID)

#define MAX(a , b) ((a > b) ? a : b) 
#define MIN(a , b) ((a < b) ? a : b)

#include <memory> //For: Allocator
#include <cstddef> //FOR: size_type


template<class T> class Vec {
    public: //interface
        typedef T* iterator;
        typedef const T* const_iterator;
        typedef size_t size_type;
        typedef T value_type;

        Vec() { create(); }
        explicit Vec(size_type n, const T& val = T()) { create(n, val); }
        Vec(const Vec& v) { create(v.begin(), v.end()); }

        Vec& operator = (const Vec&);
        ~Vec() { uncreate(); }
        T& operator[] (size_type i) { return data[i]; }
        const T& operator[] (size_type i) const { return data[i]; }

        void push_back(const T& t) {
            if (avail == limit)
                grow();
            unchecked_append(t);
        }

        void clear();
        bool erase(size_type i);

        size_type size() const { return avail - data; }

        iterator begin() { return data; }
        const_iterator begin() const { return data; }

        iterator end() { return avail; }
        const_iterator end() const { return avail;  }
    private: //implementation
        iterator data;
        iterator avail;
        iterator limit;

        std::allocator<T> alloc;

        void create();
        void create(size_type, const T&);
        void create(const_iterator, const_iterator);

        void uncreate();

        void grow();
        void unchecked_append(const T&);
};

template <class T> bool Vec<T>::erase(size_type i){
    //No, doesn't work at all
    if (i > size())
        return false;
    alloc.destroy(data+ i);
    return true;
    //Implement move-back

}

template <class T> void Vec<T>::clear(){
    uncreate(); //Destroys all objects and deallocates all addresses
    grow(); //Allocates addresses for next use.
}

template <class T> void Vec<T>::create()
{
    data = avail = limit = 0;
}

template <class T> void Vec<T>::create(size_type n, const T& val)
{
    data = alloc.allocate(n);
    limit = avail = data + n;
    std::uninitialized_fill(data, limit, val);
}

template <class T>
void Vec<T>::create(const_iterator i, const_iterator j){
    data = alloc.allocate(j - i);
    limit = avail = std::uninitialized_copy(i, j, data);
}

template <class T> void Vec<T>::uncreate()
{
    if (data){
        iterator it = avail;
        while (it != data)
            alloc.destroy(--it);
        alloc.deallocate(data, limit - data);
    }
    data = limit = avail = 0;
}

template <class T> void Vec<T>::grow()
{
    size_type new_size = MAX(2 * (limit - data), ptrdiff_t(1));
    iterator new_data = alloc.allocate(new_size);
    iterator new_avail = std::uninitialized_copy(data, avail, new_data);

    uncreate();

    data = new_data;
    avail = new_avail;
    limit = data + new_size;
}

template <class T> void Vec<T>::unchecked_append(const T& val)
{
    alloc.construct(avail++, val);
}

template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& rhs)
{ //rhs = right hand side
    if (&rhs != this){
        uncreate();
        create(rhs.begin(), rhs.end());
    }
    return *this;
}
#endif

的main.cpp

#include "Vec.h"
#include <iostream>
#include "Windows.h"
#include <string>

int main(){
    Vec<double> vector;
    for (int i = 0; i < 10; i++){
        vector.push_back((double) i);
    }

    //Check Copy Constructor
    Vec<double> vector2 = vector;



    Vec<double> vector3;
    Vec<double> vector4;
    for (int i = 0; i < 10; i++){
        vector3.push_back((double)(i*2));
    }

    //Check Assignment Operator
    vector4 = vector3;



    for (int i = 0; i < 10; i++){
        std::cout << vector[i] << " " << vector2[i] << " " << " " << vector3[i] << " "
            << vector4[i] << std::endl;
    }

    //Check Erase Operator
    vector.erase(3);

    for (int i = 0; i < vector.size(); i++){
        std::cout << vector[i] << std::endl;
    }

    std::system("PAUSE");
    return 0;
}

主要是&#34;向量&#34;是我正在测试它的对象。它被初始化为数字0到9,并且在调用vector.erase(3)之后保持不变;

另外,对于迭代器的解释,数据指向列表的开头(第一个元素),有效指向初始化元素结束的位置,并限制未初始化存储结束的点。数据&lt; = avail&lt; =始终限制。 所以在erase()方法中,我假设数据+ i是我需要销毁的东西。删除后,使用avail-1。

2 个答案:

答案 0 :(得分:0)

这似乎是作业,所以这不是一个完整的答案。

但我会提示。

您对解决方案的初步猜测存在很大问题。一旦销毁data[i],该位置现在(或应被视为)现在不能用作T类型的对象。特别是,您不能使用data[i] = data[i+1],因为赋值运算符的知识可能已被破坏。

标准补救措施适用:不要那么做。

您已经发现需要将元素i上方的每个元素向下移动一个。所以在你摧毁任何东西之前先这样做。 (这是一个很大的暗示。)当你完成时,可能会有一些东西需要被摧毁(另一个大的暗示)。

顺便说一句,我上面提到的解决方案效率不高。在C ++ 11中有更有效的方法可以做到这一点。

答案 1 :(得分:-1)

这是我如何使擦除功能正常工作:

template <class T> bool Vec<T>::erase(size_type i)
    {
        if (i > size())
            return false;
        //alloc.destroy(data + i); => not necessary, assuming T has a proper assignment operator defined

        for (int i = 0; i < size() - 1; i++)
            data[i] = data[i + 1];

        avail--; //decrease the size of the vector
        alloc.destroy(data + size());
        return true;
    }

您可能需要查看Difference between "destroy" "destructor" "deallocate" in std::allocator?,了解allocator.destroy的功能。