free():实现向量的erase()时指针无效

问题描述 投票:2回答:1

我正在尝试实现Vector类。对于初学者,我的Vector还不支持泛型,只有我的Thing类。

Vector类应该支持:empty(),push_back(),erase()和下标运算符。如果需要,它还应相应调整大小。

我的Vector实现:

class Vect {
public:
    Vect() {
        length = 0;
        capacity = 3;
        charCnt = 0;
        data = new Thing[capacity];
    }
    ~Vect() {
        delete[] data;
    }

    bool empty() const {
        return length == 0;
    }

    void push_back(const char *str) {
        if(length + 1 == capacity)
            doubleSize();
        data[length++] = Thing(str);
        charCnt += strlen(str);
    }

    bool erase(size_t at) {
        if(at >= length)
            return false;

        auto newData = new Thing[capacity];
        size_t newIndex = 0;

        for(size_t i = 0; i < at; i++)
            newData[newIndex++] = data[i];

        for(size_t i = at + 1; i < length; i++)
            newData[newIndex++] = data[i];

        //free(): invalid pointer
        delete[] data;
        data = newData;

        return true;
    }

    const char* operator[](unsigned int index) {
        return data[index].getStr();
    }

    char* toString() {
        auto result = make_shared<char *>(new char[charCnt + 1]);
        size_t resultIndex = 0;

        for(size_t dataIndex = 0; dataIndex < length; dataIndex++) {
            auto patchOffset = data[dataIndex].getO();
            auto patchLength = data[dataIndex].getL();
            for(size_t patchIndex = patchOffset; patchIndex < patchLength; patchIndex++)
                (*result.get())[resultIndex++] = data[dataIndex].getStr()[patchIndex];
        }

        (*result.get())[resultIndex] = '\0';
        return *result.get();
    }

private:
    size_t length, capacity, charCnt;
    Thing *data;

    void doubleSize() {
        size_t newCapacity = capacity*2;
        auto newData = new Thing[newCapacity];

        for(size_t i = 0; i < length; i++) {
            newData[i] = data[i];
        }

        //this works
        delete[] data;
        data = newData;
    }
};

当我尝试实施erase()时,我遇到了问题。我的实现是直截了当的:erase()接受一个参数,即应该删除元素的索引。所以我创建了一个新数组,将所有内容复制到erasion-index,跳过索引,然后复制其余内容。然后我删除旧数组并为变量分配新数组。

我在doubleSize()方法中做了一些非常相似的事情,这似乎工作正常(检查valgrind)。

我遇到的问题是delete[]不适用于data

我使用的测试环境:

class Thing {
public:
    Thing() {
        o = 0;
        l = 0;
        ptr = nullptr;
    }

    explicit Thing(const char *str) {
        ptr = str;
        o = 0;
        l = strlen(str);
    }

    Thing(const Thing &other) {
        this->o = other.o;
        this->l = other.l;
        this->ptr = other.ptr;
    }
    friend void swap(Thing &first, Thing &other) {
        using std::swap;
        swap(first.o, other.o);
        swap(first.l, other.l);
        swap(first.ptr, other.ptr);
    }
    Thing& operator=(Thing other) {
        swap(*this, other);
        return *this;
    }

    Thing(Thing &&other) noexcept: Thing() {
        swap(*this, other);
    }

    size_t getO() const {
        return o;
    }
    size_t getL() const {
        return l;
    }

    const char* getStr() const {
        return ptr;
    }
private:
    size_t o, l;
    const char *ptr;
};

//class Vect...

int main() {
    Vect s; char tmpStr[100];

    assert(s.empty());

    s.push_back("hello ");
    s.push_back("world");
    s.push_back("!");
    s.push_back(" this ");
    s.push_back("is ");
    s.push_back("me!");

    strncpy(tmpStr, "hello world! this is me!", sizeof(tmpStr));
    assert(stringMatch(s.toString(), tmpStr));

    s.erase(2);
    strncpy(tmpStr, "hello world this is me!", sizeof(tmpStr));
    assert(stringMatch(s.toString(), tmpStr));
}

咨询调试器后,我发现了以下内容:

  1. 第一个for循环工作正常,因为内容涉及。
  2. 第二个循环:在第二次迭代后,在赋值 - data[0]被破坏后 - ol变量获得随机值,而ptr仍然指向正确的字符串。
  3. 完成第二次循环的最后一次迭代后,ptr现在变为NULLdata[1]现在具有随机的ol值。

之后delete []被调用,这不会引起错误,因为我用分配的内存做了一些狂野的竞技场。

我在哪里管理不善?

c++ vector memory-management
1个回答
5
投票

doubleSize分配新缓冲区,但不更新capacity成员。 Oncelength已经过了capacity,你甚至都不会再发现溢出了。最终,你会溢出真实。


虽然您正在使用erase进行擦除,但这会减小向量的大小,但它不会减少您的length变量。

© www.soinside.com 2019 - 2024. All rights reserved.