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

时间:2019-04-13 23:48:39

标签: c++ vector memory-management

我正在尝试实现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()接受一个参数,即应从该索引处删除元素。因此,我创建了一个新数组,将所有内容复制到擦除索引,然后跳过索引,然后复制其余内容。然后删除旧数组,然后将新数组分配给变量。

我在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. first for循环效果很好。

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

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

然后调用delete [],因为我对分配的内存进行了一些狂放的牛仔竞技表演,因此会意外触发错误。

我在哪里管理内存不足?

1 个答案:

答案 0 :(得分:5)

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


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