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

Le *_*Anh 2 c++ memory-management vector

我正在尝试实现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;
    }
};
Run Code Online (Sandbox Code Playgroud)

尝试实施时遇到了问题erase()。我的实现很简单:erase()采用一个参数,即应删除元素的索引。因此,我创建了一个新数组,将所有内容复制到擦除索引,跳过索引,然后复制其余内容。然后删除旧数组,并将新数组分配给变量。

我在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));
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 首先for循环可以很好地处理内容。

  2. 第二环:第二次迭代后,在转让之后- data[0]被破坏- ol变量,同时获得随机值ptr仍然指向正确的字符串。

  3. 在完成第二个循环的最后一次迭代后,ptr现在变为NULL并且data[1]现在具有随机ol值。

之后delete []被调用,这会意外地触发错误,因为我对分配的内存做了一些狂放的牛仔竞技表演。

我在哪里管理不当?

Igo*_*nik 5

doubleSize分配新的缓冲区,但不更新capacity成员。一旦length过去capacity,您甚至不再检测到溢出。最终,您将真正地溢出。


尽管您正在使用擦除erase,但这会减小向量的大小,但是不会减少length变量的数量。