C++, Указатели. delete

134
24 мая 2019, 15:20

Вопрос в следующем(т.к. разные данные\книги\статьи etc. не дают четкого ответа): Есть указатель. Много. Листы с указателями, прочие "вкусности". Необходимо ли удалять указатели в C++17? Освобождает ли ОС память, занятую указателями, если их не удалить, при завершении программы?

Вторая часть вопроса касается непосредственно delete ptr; Если указатель указывает на класс\структуру, хранящую в себе другие указатели, правильно ли, что при удалении указателя на объект вызовется деструктор объекта, где нужно будет удалить его указатели?

Answer 1

Освобождает ли ОС память, занятую указателями, если их не удалить, при завершении программы

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

Если указатель указывает на класс\структуру, хранящую в себе другие указатели, правильно ли, что при удалении указателя на объект вызовется деструктор объекта, где нужно будет удалить его указатели?

Будет вызван деструктор. Но будет ли он освобождать память, которая была выделена и на которую указывают указатели в объекте - зависит от того, как именно вы его напишете. Генерируемый деструктор по умолчанию с "сырыми" указателями работать не умеет и ничего не освобождает. Другое дело - если члены представляют собой указатели интеллектуальные типа unique_ptr - тут деструктор по умолчанию вызовет деструкторы для таких членом.

Update
В комментарии места мало.

void f()
{
    int * p = new int[5];
    ...
}

Память, занятая переменной p, находится в стеке и, понятно, при выходе из функции освобождается.

Память, занятая пятью int'ами - на которую указывает указатель p - автоматически при выходе из функции не освобождается. Только при вызове delete[]p; или по завершению всей программы.

Answer 2

По-моему, вы путаете указатели с объектами, на которые указатель указывает. Указатель - это переменная. Он, конечно, тоже может создаваться динамически и в этом случае его надо удалять. Но обычно работа с указателем выглядит так:

SomeType* ptr = new SomeType;

Здесь указатель ptr - это переменная, созданная в стеке. Удалять этот указатель не нужно (и не выйдет), он будет удаляться автоматически. Ему присвоен адрес переменной типа SomeType, созданной в динамической памяти, и ее нужно будет удалить:

delete ptr;

Здесь удаляется не указатель, а объект, на который он указывает. Сам указатель ptr после вызова delete останется неизменным и будет указывать на удаленный объект. Использование его приведет к ошибке.

Обычно указатель указывает на объект в динамической памяти, но далеко не всегда. Он может указывать на статический или стековый объект. И в этом случае удалять тоже ничего не нужно:

int j;
int* p = &j;

Здесь есть момент, связанный с библиотечными классами unique_ptr и shared_ptr. Если обычному указателю все равно на что указывать, эти классы могут работать только с динамической памятью. Зато они автоматически выполняют удаление объекта.

Освобождает ли ОС память, занятую указателями, если их не удалить, при завершении программы?

При завершении процесса система освобождает все связанные с ним ресурсы. Иногда это можно использовать, когда удаление перед завершением работы выполняется достаточно нетривиально, и просто нет смысла писать сложную процедуру для того, что и так будет сделано системой на следующем шаге. Но злоупотреблять этим не стоит, это все же дурной стиль.

Если указатель указывает на класс\структуру, хранящую в себе другие указатели, правильно ли, что при удалении указателя на объект вызовется деструктор объекта, где нужно будет удалить его указатели?

Если объект содержит указатели, он должен иметь деструктор, который освободит указываемые ими объекты. Если он содержит просто вложенные объекты, о них заботиться не нужно, они будут освобождены автоматически (с вызовом деструкторов, если необходимо). Это же относится к библиотечным классам указателей unique_ptr и shared_ptr - система автоматически вызовет их деструкторы, которые и освободят связанные объекты:

class A {
. . .
};
class B {
  public:
    char* pch;             // Это простой указатель на строку, его нужно освободить самому
    unique_ptr<int> pint;  // Библиотечный указатель будет освобожден автоматически
    A a;                   // Об экземпляре класса тоже позаботится система
    A* pa;                 // А об этом экземпляре A придется заботиться самому
  ~B() { delete[] pch; delete A; }   // Освободить связанные объекты
};

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

Answer 3
template <class T>
void f()
{
   T obj;   // занята память sizeof(T)        
   T* ptr1 = nullptr,  // занята память на одно машинное слово
           ptr2 = nullptr; //  занята память на одно машинное слово
   T* ptr1 = &obj;   // первый указатель хранит адрес временного обьекта
   T* ptr2 = new T;  // занята память sizeof(T)  
}

после вызова функции, память, занимаемая указательями и локальным обьектом освободится, а вот память, занимаемая обьектом, созданным в куче, не освободится (будет занята память на sizeof(T) ) , Ее нужно освобождать в ручную: // delete ptr2;

READ ALSO
Что делает строка virtual ~Figure() {};?

Что делает строка virtual ~Figure() {};?

Что делает эта строка:

113
Сортировка по русскому алфавиту С++

Сортировка по русскому алфавиту С++

В задании по С++ сказанно отсортирвать массив названий товаров продуктового магазина по алфавиту на русскомС английской сортировкой в Java мне...

131
Перегрузка операции ==

Перегрузка операции ==

Есть класс и у него перегружена операция ==

128
Как внести имя шаблона в класс с помощью using declaration?

Как внести имя шаблона в класс с помощью using declaration?

Чтобы внести имя зависимого типа из базового класса в класс-наследник с помощью using declaration, нужно явно указать ключевое слово typenameНо как внести...

126