Освобождаване на последния елемент от динамичен масив

аз имам

int * array=new int[2];

и бих искал да освободя паметта на последния елемент, като по този начин намаля разпределената памет само до 1 елемент. Опитах се да се обадя

delete array+1;

но дава грешка

*** glibc открит *** skuska: free(): невалиден указател: 0x000000000065a020 *

Може ли това да се направи в C++03 без изрично преразпределение?

Забележка: Ако исках да използвам клас вместо примитивен тип данни (като int), как мога да освободя паметта, така че да се извика и деструкторът на класа?

Забележка 2: Опитвам се да внедря vector::pop_back


person Slazer    schedule 04.11.2012    source източник
comment
Защо просто не можете да използвате vector::pop_back? Повторното изобретяване на колелото ще ви създаде повече проблеми, отколкото си струва.   -  person Casey    schedule 04.11.2012
comment
Опитвам се да внедря подобен на вектор клас като упражнение по програмиране и сега внедрявам vector::pop_back. Използвах int* p_array_of_values като основна структура от данни.   -  person Slazer    schedule 04.11.2012


Отговори (4)


Не използвайте израз new[] за това. Векторът не работи така. Това, което правите, е да разпределите част от сурова памет. Можете да използвате malloc за това или можете да използвате оператор new, който е различен от новия израз. Това по същество прави функцията-член reserve() на std::vector, ако приемем, че сте използвали разпределителя по подразбиране. Той не създава никакви реални обекти по начина, по който го прави изразът new[].

Когато искате да конструирате елемент, използвате placement new, като му предавате местоположение някъде в необработената памет, която сте заделили. Когато искате да унищожите елемент, извиквате директно неговия деструктор. Когато сте готови, вместо да използвате израза delete[], използвате operator delete, ако сте използвали operator new, или използвате free(), ако сте използвали malloc.

Ето пример за създаване на 10 обекта и унищожаването им в обратен ред. Бих могъл да ги унищожа във всякакъв ред, но ето как бихте го направили във векторна реализация.

int main()
{
    void * storage = malloc(sizeof(MyClass) * 10);

    for (int i=0; i<10; ++i)
    {
        // this is placement new
       new ((MyClass*)storage + i) MyClass;
    }

    for (int i=9; i>=0; --i)
    {
        // calling the destructor directly
        ((MyClass*)storage + i)->~MyClass();
    }

    free(storage);
}

pop_back ще бъде имплементирано чрез просто извикване на деструктора на последния елемент и намаляване на променливата член size с 1. Това не би, не трябва (и не може, без да прави куп ненужни копия) да освободи никаква памет.

person Benjamin Lindley    schedule 04.11.2012

Няма такъв вариант. Единственият начин за преоразмеряване на масива е заделяне на нов масив с размер old_size - 1, копиране на съдържанието на стария масив и след това изтриване на стария масив.

Ако искате свободна обектна памет, защо не създадете масив от указатели?

MyClass **arr = new MyClass*[size];
for(int i = 0; i < size; i++)
 arr[i] = new MyClass;

// ...

delete arr[size-1];
person Hauleth    schedule 04.11.2012
comment
Това е опция, но вече използвах Използвах int* p_array_of_values като основна структура от данни. Както и да е, вече намерих някои хубави решения тук. - person Slazer; 04.11.2012

std::vector::pop_back не преразпределя нищо, а просто актуализира вътрешната променлива, определяща размера на данните, намалявайки я с единица. Старият последен елемент все още е там в паметта; векторът просто не ви позволява достъп до него чрез публичния му API. *

Това, както и че нарастващото преразпределение е нелинейно, е основата защо std::vector::capacity() не е еквивалентно на std::vector::size().

Така че, ако наистина се опитвате да изобретите отново std::vector по някаква причина, отговорът на въпроса ви за преразпределението е недей.


* Всъщност за непримитивните типове данни е малко по-сложно, тъй като такива елементи са семантично унищожени, въпреки че тяхната памет няма да бъде освободена.

person Lightness Races in Orbit    schedule 04.11.2012

Тъй като използвате C++03, имате достъп до типа данни std::vector. Използвайте това и това е едно обаждане:

#include <vector>
//...
std::vector<int> ary(3);
//...
ary.erase(ary.begin() + (ary.size() - 1));

or

#include <vector>
//...
std::vector<int> ary(3);
//...
ary.pop_back();

РЕДАКТИРАНЕ:

Защо се опитвате да изобретите отново колелото? Просто използвайте vector::pop_back.

Както и да е, деструкторът се извиква върху съдържащите се типове данни САМО ако съдържащият се тип данни НЕ Е указател. Ако Е указател, трябва ръчно да извикате delete на обекта, който искате да изтриете, задайте го на nullptr или NULL (тъй като опитът за извикване на delete на предишно изтрит обект е лош, извикването на delete на нулев указател не е операция) , след това извикайте изтриване.

person Casey    schedule 04.11.2012