2014-10-15 5 views
0

Я создал вектор, выделив его память в кучу. Затем я создаю 10 строковых объектов, также выделенных для памяти кучи и сохраняя их внутри вектора. Я попытался освободить эту память, связанную с каждым новым строковым объектом, используя оператор delete, но я не знаю, как это сделать. Я использую C++ 11.Освобождение памяти, выделенной куче, хранящейся внутри вектора

#include <vector> 
#include <string> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    vector<string> *v = new vector<string>; 

    for(int i = 0; i < 10; i++) { 
    // allocate a new string object on the heap 
    string *a = new string("Hello World"); 
    //de-reference the string object 
    v->push_back(*a); 
    } 

    // show the contents of the vector 
    for(auto i = v->begin(); i != v->end(); ++i) { 
    // okay so this makes a lot more sense than: 
    // const string &s = *i; 
    // this way we create a pointer to a string object 
    // it is a lot more clear this way 
    const string *s = &(*i); 
    cout << *s << " " << s->length() << endl; 
    } 

    cout << endl << endl; 

    for(vector<string>::iterator it = v->begin(); it != v->end(); ++it) { 
    delete &it; 
    v->erase(it); 
    } 

    for(auto i = v->begin(); i != v->end(); ++i) { 
    cout << *i << endl; 
    } 
    cout << endl << "Size: " << v->size() << endl; 

    delete v; 
} 

g++ -std=c++11 main.cc -o main

Моя ошибка в том, что не все объекты удаляются. В итоге у меня осталось 5 объектов, оставшихся после последнего 4-го утверждения. Когда эти операции выполняются, я ожидаю, что в векторе будут нулевые объекты.

Мой выход:

Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 
Hello World 11 


Hello World 
Hello World 
Hello World 
Hello World 
Hello World 
Size: 5 

Проблема заключается в том, что не все объекты удаляются.

+7

Почему вы выделяете вектор динамически? Для этого нет оснований. Вам вообще не нужно выполнять ручное управление памятью. – juanchopanza

+1

Это для образовательных целей. То, что я делаю, бесполезно, за исключением того, что я участвую. –

+0

Просто удалите все указатели и все ваши новости, и он будет работать так, как вы этого хотите. – IdeaHat

ответ

2

Проблема в том, что не все объекты удаляются.

Это потому, что вы просачиваете струны при заполнении вектора.

for(int i = 0; i < 10; i++) { 
    string *a = new string("Hello World"); // Leak: who deletes a? Nobody! 
    v->push_back(*a); 
} 

Это будет способ избежать этой конкретной утечки:

for(int i = 0; i < 10; i++) { 
    v->push_back("Hello World"); 
} 

Тем не менее, вы динамически выделяя v, и нет никаких причин, чтобы сделать это. Это было бы более простым и идиоматическим способом сделать это на C++. Это не связано с утечками памяти или неопределенное поведение, в отличие от версии:

int main() 
{ 
    vector<string> v(10, "Hello World"); 

    for (auto& s : v) 
    cout << s.length() << endl; 
} 

Вам не нужно указателей. Вам не нужно звонить new. Вам не нужно беспокоиться об управлении памятью.

1

Вы нажимаете копию строки на векторе, когда используете v-> push_back (* a); команда. Если вы не хотите этого делать, вам нужно сделать свой вектор вектором указателей на строку, а затем направить указатели на вектор.

5

Я думаю, что конкретная проблема, вы беспокоитесь о том, в мириады проблемы вашей цикл не удаляя все элементы, то есть:

for(vector<string>::iterator it = v->begin(); it != v->end(); ++it) { 
    delete &it; 
    v->erase(it); 
} 

Ваш вопрос в том, что вы меняете вектор вы итерация , что приводит к неопределенному поведению, которое проявляется, поскольку вы не удаляете все значения. (Итератор вектора действителен только до тех пор, пока вы не добавляете и не удаляете значения).

I может объяснить, почему это всего лишь 5, но ответ не будет кросс-платформой. В этом случае компилятор может делать все, что захочет. Для компилятора было бы справедливо заставить демонов вылететь из вашего носа.

В принципе, вы удаляете значение, и они переходят к следующему индексу. Таким образом, вы удаляете whats at 0, который будет тянуть то, что находится на 1, до 0. Затем вы переходите к индексу 1, который содержит то, что раньше было в индексе 2. Затем вы удаляете его. По сути, вы удаляете все четные индексы из вектора.

Edit: Сведение задачи к минимальному воспроизводимое:

std::vector<int> vals; 

for (int i = 0; i < 11; i++0) vals.push_back(i); 

for (std::vector<int>::iterator i = vals.begin(); i != vals.end(); ++i) 
{ 
    vals.erase(i);//after this point, i's behavior is undefined! 
} 

Edit 3: Перечисление всех проблем с кодом (предлагаемые действия на основе Meta разговора)

  1. Vector уже выделяет свою коллекцию памяти в куче (все, кроме указателя на память и счетчики размера). Весь этот подход основан на не понимании этого факта. новый вектор действительно помещает весь вектор в кучу, что может быть тем, чего хочет OP.
  2. строка также выделяет память в куче. new std::string просто выделит указатель на массив символов и размер в куче. Так что и то, что нужно отметить.
  3. Если вы хотите иметь вектор указателей, вы должны иметь std::vector<T*>. std::vector<T> будет вектором экземпляров T.
  4. Поскольку память вектора уже выделена в куче, это изначально казалось бы попыткой бесполезности. Однако существуют случаи, когда вы МОЖЕТЕ хотеть такую ​​структуру. Например, полиморфный тип должен храниться таким образом, чтобы избежать нарезки. (Однако я бы использовал умный указатель, но для образовательных целей это хорошее упражнение).
  5. В цикле 1 вы динамически выделяете строку. Затем вы используете конструктор копирования для создания экземпляра в векторе. Затем вы можете вывести указатель из области видимости. Это утечка памяти и неэффективная копия.
  6. Вы, похоже, действительно хотите использовать указатели в качестве ссылок. std::string& s = *it гораздо читабельнее, чем адрес.
  7. juanchopanza правильно отметил, что вы удаляете местоположение итератора, что является более неопределенным поведением. &it имеет тип std::vector<string>::iterator*. Почему это, вероятно, не сбой, а сжигание - это то, что тип итератора состоит только из встроенных типов, и после этого вы не слишком много делаете (вы, вероятно, настроились на какое-то восхитительное повреждение стека). Если у вас std::vector<T*> (соответствующий пункт 1) с вектором, имеющим исключительное право собственности, вы хотите сделать это, чтобы очистить его:

delete &(*it);

Но было бы гораздо лучше, чтобы сделать что-то вроде std::vector<std::unique_ptr<T>> и не беспокойтесь об этом.

  1. Никогда не применяйте глобальное использование std.
+0

Интересно, что 'delete ⁢' делает. Это не может быть хорошо. – juanchopanza

+2

@juanchopanza хорошая точка ... что происходит, БОЛЬШЕ НАСАЛЬНЫЕ ДИАМАНТЫ. – IdeaHat

+0

'delete & (* it)' так же плохо, как 'delete & it'. Оба они уничтожают объект, которым управляет кто-то другой. Если у вас есть вектор указателей, то «delete * it» может быть правильным. –

0

Я не понимаю, почему вы выделяете строку динамически, даже если вы храните ее в векторе по значению. Этот фрагмент не имеет смысла даже для образовательных целей, если вы не научитесь утечки памяти на C++.

for(int i = 0; i < 10; i++) { 
    // allocate a new string object on the heap 
    string *a = new string("Hello World"); 
    //de-reference the string object 
    v->push_back(*a); 
    } 

Если вы хотите хранить указатели в любом СТЛ контейнере, то я предложил бы использовать некоторые умные указатели (за исключением auto_ptr), как повышение :: shared_ptr. Они заботятся об управлении памятью для вас.

Смежные вопросы