2012-04-20 2 views
8

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

Рассмотрим такую ​​ситуацию:

struct CoordLocation 
{ 
    float X; 
    float Y; 
    float Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

Будет призывающую удалить также очистить память, используемую в полях X, Y, Z? В некоторых ответах, которые я нашел, упоминалось, что я просто удалю POINTER, а не объект, на который ссылается этот объект. Что делать, если ...

struct CoordLocation 
{ 
    float *X; 
    float *Y; 
    float *Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

А что, если я вручную освободить память для каждого объекта внутри структуры в конструктор/деструктор?

struct CoordLocation 
{ 
    CoordLocation() 
    { 
     *X = new float; 
     *Y = new float; 
     *Z = new float; 
    } 
    ~CoordLocation() 
    { 
     delete X; delete Y; delete Z; 
    } 
    float *X; 
    float *Y; 
    float *Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

Я заметил, что для простой ситуации, такие как:

float *a = new float; 
    *a = 5.0f; 
    printf("%f", *a); 
    delete a; 
    printf("%f", &a); 

Printf напечатает 5.0, так что переменная, на которую указывает не совсем разрушен.

Итак, мой вопрос: Как я могу надежно освобождать (как в случае утечек памяти) ВСЕ память, используемая структурой в этом случае?

struct CoordLocation 
{ 
    float X; 
    float Y; 
    float Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

Спасибо!

+0

Ваш второй 'printf' является немного неправильным (идентификатор формата не соответствует типу аргумента). Вы действительно хотели написать 'printf ("% f ", * a);', как в первом 'printf'? –

+0

Если вы используете недавний/современный компилятор, самым надежным способом гарантировать, что ** новая ** исправленная память будет удалена правильно, - использовать один из «умных» типов указателей, доступных в стандартной библиотеке C++. std :: unique_ptr и std :: shared_ptr получают полное право собственности на новый объект и гарантируют, что объект, за который они несут ответственность, не будет просочиться. (В идеале у вас никогда не будет указателей _raw_ в любом месте вашего кода) –

+0

Спасибо за подсказку на unique_ptr. Я работаю с Visual C++ 2008, поэтому компилятор не поддерживает новый стандарт (не может найти unique_ptr в пространстве имен std). Я поеду со старыми способами, по крайней мере, до тех пор, пока это не произойдет позже. –

ответ

18

Вам нужно всего лишь delete память, которую вы выделили new.

printf будет печатать 5.0, поэтому переменная, на которую указывает а, не точно уничтожена.

Вы фактически сталкиваетесь с неопределенным поведением. Хотя значение все еще существует, память была выпущена и может быть повторно использована.

Так следующее:

struct CoordLocation 
{ 
    float X; 
    float Y; 
    float Z; 
}; 

не может создать утечку памяти, если опустить деструктор.

Ваш следующий фрагмент:

struct CoordLocation 
{ 
    float *X; 
    float *Y; 
    float *Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

потенциально может создать утечку памяти, но не так, как есть. Нижеследующее:

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    coord->X = new float(); 
    delete coord; 
    return 0; 
} 

Ваш третий пример

struct CoordLocation 
{ 
    CoordLocation() 
    { 
     *X = new float; 
     *Y = new float; 
     *Z = new float; 
    } 
    ~CoordLocation() 
    { 
     delete X; delete Y; delete Z; 
    } 
    float *X; 
    float *Y; 
    float *Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

не будет создавать утечку памяти, потому что вы все бесплатно памяти, что вы выделяете. Если вы должны были упустить деструктора или забыть позвонить по номеру delete coord;, у вас будет утечка памяти.

Хорошее эмпирическое правило: позвоните по номеру delete за каждые new и delete[] за каждые new[], и вы в безопасности.

+1

Возможно, «БУДЕТ повторно использован». Сказал угрожающим тоном, держа под светом фонарик под подбородком. – mjfgates

+0

Лучиан, что происходит в третьем примере, если второе или третье «новое» исключение? В 'main' нет обработчиков исключений, поэтому' terminate' и 'abort' будут выполнены, и программа будет завершена, но до этого не будет утечки памяти как' delete coord; '(и поэтому 'delete X; ') никогда не выполняется? –

+0

@BojanKomazec AH! Это зависит от того, что вы подразумеваете под утечкой памяти - http: // stackoverflow.com/questions/9921590/is-not-call-delete-on-a-dynamic-alloc-object-always-a-memory-leak –

0

В этом примере:

struct CoordLocation 
{ 
    float X; 
    float Y; 
    float Z; 
}; 

int main() 
{ 
    CoordLocation *coord = new CoordLocation(); 
    delete coord; 
    return 0; 
} 

Память освобождается. В этом коде нет утечек. На примере, который вы указали, где ваша структура содержит указатели, пока вы освобождаете их в деструкторе (как и в вашем примере), утечек не будет.

В этом фрагменте:

float *a = new float; 
*a = 5.0f; 
printf("%f", *a); 
delete a; 
printf("%f", &a); 

Значение a остается неизменным после удаления, так как delete (ING) указатель будет только пометить адрес памяти, как «освобожденный», он не будет изменять его содержимое , Доступ к свободным указателям - это неопределенное поведение.

0

Указатель сам выделяется с автоматическим временем хранения, там нет ничего свободного. Структура - это те три поля, которые освобождаются при вызове delete. Вы только звоните delete на что-то, что было возвращено new.

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

Одна вещь, чтобы отметить, однако, такой случай (в C++ вы бы не создать тип, как это, но пример актуален):

struct Foo { 
    char *str; 
}; 

Foo *f = new Foo(); 
f->str = new char[10]; 

delete f; 

В этом случае у вас есть утечка. Вы удаляете f, который состоит из достаточного количества памяти для хранения одного char*, но то, что указывает char*, было также динамически распределено. Таким образом, вы должны освободить его тоже:

delete f->str; 
delete f; 

Опять же, в C++ вы, вероятно, не проектировать печатаю этот путь, а не в пользу типов, как std::string и принципы, такие как RAII, но пример актуален.

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