Какой оператор необходимо вызвать для удаления массива
Перейти к содержимому

Какой оператор необходимо вызвать для удаления массива

  • автор:

Оператор delete (C++)

Аргумент cast-expression должен быть указателем на блок памяти, ранее выделенный для объекта, созданного с помощью нового оператора. Оператор delete имеет результат типа void и поэтому не возвращает значение. Например:

CDialog* MyDialog = new CDialog; // use MyDialog delete MyDialog; 

Использование delete указателя на объект, не выделенный с new непредсказуемыми результатами. Однако можно использовать delete указатель с значением 0. Эта подготовка означает, что при new возврате 0 при сбое удаление результата неудачной new операции является безвредным. Дополнительные сведения см. в разделе «Новые и удаленные операторы».

delete Операторы new также можно использовать для встроенных типов, включая массивы. Если pointer ссылается на массив, поместите пустые квадратные скобки ( [] ) перед pointer :

int* set = new int[100]; //use set[] delete [] set; 

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

Если delete используется для освобождения памяти для объекта класса C++, деструктор объекта вызывается до освобождения памяти объекта (если объект имеет деструктор).

Если операнду оператору delete является изменяемым l-значением, его значение не определено после удаления объекта.

Если указан параметр компилятора /sdl (включение дополнительных проверка безопасности), операнду delete оператору присваивается недопустимое значение после удаления объекта.

Использование оператора delete

Существует два синтактических варианта для оператора удаления: один для отдельных объектов и другой для массивов объектов. В следующем фрагменте кода показано, как они отличаются:

// expre_Using_delete.cpp struct UDType < >; int main() < // Allocate a user-defined object, UDObject, and an object // of type double on the free store using the // new operator. UDType *UDObject = new UDType; double *dObject = new double; // Delete the two objects. delete UDObject; delete dObject; // Allocate an array of user-defined objects on the // free store using the new operator. UDType (*UDArr)[7] = new UDType[5][7]; // Use the array syntax to delete the array of objects. delete [] UDArr; >

В следующих двух случаях возникают неопределенные результаты: использование формы удаления массива ( delete [] ) в объекте и использование неаррейской формы удаления в массиве.

Пример

Примеры использования delete см . в разделе «Новый оператор».

Принцип работы delete

Оператор удаления вызывает удаление оператора функции.

Для объектов не типа класса (класса, структуры или объединения) вызывается глобальный оператор удаления. Для объектов типа класса имя функции deallocation разрешается в глобальных область если выражение удаления начинается с унарного оператора разрешения область ( :: ). В противном случае перед освобождением памяти оператор удаления вызывает деструктор объекта (если указатель не имеет значения null). Оператор удаления можно определять отдельно для каждого класса; если для некоторого класса такое определение отсутствует, вызывается глобальный оператор удаления. Если выражение удаления используется для освобождения объекта класса, статический тип которого имеет виртуальный деструктор, функция освобождение разрешается через виртуальный деструктор динамического типа объекта.

Оператор Erase

Повторно инициализирует элементы массивов фиксированного размера и освобождает место для хранения динамического массива.

Синтаксис

Очисткасписка массивов

Обязательный аргументarraylist — это одна или несколько переменных массива с разделителями-запятыми для удаления.

Замечания

Оператор удаления Erase ведет себя по-разному в зависимости от того, какой массив используется: с фиксированным размером (обычный) или динамический. Erase не освобождает память для массивов фиксированного размера. Erase задает элементы фиксированного массива следующим образом.

Тип массива Влияние очистки на элементы фиксированного массива
Фиксированный числовой массив Задает нулевое значение для каждого элемента.
Фиксированный массив строк (переменной длины) Задает строку нулевой длины («») для каждого элемента.
Фиксированный массив строк (фиксированной длины) Задает нулевое значение для каждого элемента.
Фиксированный вариантный массив Задает пустое значение для каждого элемента.
Массив типов, определяемых пользователем Задает для каждого элемента значение как для отдельной переменной.
Массив объектов Задает особое значение Nothing (Ничего) для каждого элемента.

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

Пример

В этом примере оператор Erase используется для повторной инициализации элементов массивов фиксированного размера и отмены выделения места для хранения динамического массива.

' Declare array variables. Dim NumArray(10) As Integer ' Integer array. Dim StrVarArray(10) As String ' Variable-string array. Dim StrFixArray(10) As String * 10 ' Fixed-string array. Dim VarArray(10) As Variant ' Variant array. Dim DynamicArray() As Integer ' Dynamic array. ReDim DynamicArray(10) ' Allocate storage space. Erase NumArray ' Each element set to 0. Erase StrVarArray ' Each element set to zero-length ' string (""). Erase StrFixArray ' Each element set to 0. Erase VarArray ' Each element set to Empty. Erase DynamicArray ' Free memory used by array. 

См. также

Поддержка и обратная связь

Есть вопросы или отзывы, касающиеся Office VBA или этой статьи? Руководство по другим способам получения поддержки и отправки отзывов см. в статье Поддержка Office VBA и обратная связь.

Оператор delete: удаление массива

Если вы выделяли память при помощи new[] , вы обязаны освободить её с помощью delete[] . Так гласит стандарт.

Освобождение такой памяти как-то по-другому ( delete без [] или вообще free ) является Undefined Behaviour. Если в программе есть Undefined Behaviour, она имеет право вести себя как угодно: может ничего плохого не делать, может вылететь в любой точке, отформатировать ваш винчестер или выбросить из окна вашего кота.

You have been warned.

Отслеживать
ответ дан 12 июн 2013 в 16:48
207k 29 29 золотых знаков 294 294 серебряных знака 528 528 бронзовых знаков

я понимаю что это по стандарту, но вот смотрите, тут после выполнения первого варианта и после второго. joxi.ru/uploads/prod/20130612/4a2/824/…

12 июн 2013 в 16:51
ну теперь понятнее, спасибо 🙂
12 июн 2013 в 16:54
Почитайте про undefined behaviour, забавная и опасная штука.
12 июн 2013 в 16:55
читаю, благодарю)
12 июн 2013 в 17:07
Палец вверх за кота 🙂
12 июн 2013 в 20:23

Если Вы выделили массив элементов, то этот массив и нужно удалить. New делает malloc на sizeof(объект) и вызывает конструктор объекта, new[] делает malloc на sizeof(объект) * кол-во_объектов, вызывает конструкторы для каждого будущего объекта и записывает информацию о том, память для скольких объектов была выделена. Куда и как эта информация записывается вопрос отдельный (плюс бывает ситуации когда это информация не нужна).

Вызывая delete Вы говорите компилятору «удали этот один элемент по такому-то адресу». Оператор delete[] же читает сколько объектов расположено в выделенной памяти (как мы помним, оператор new[] сохранил это число в процессе своей работы), вызывает для каждого их них деструктор, а после вызывает free() , «отдавая память назад ОС». Именно поэтому для памяти, выделенной через new/new[] нужно вызывать delete/delete[] соответственно. Контроль за тем, что для оператора выделения должен быть вызван соответствующий оператор освобождения лежит на программисте.

Отслеживать
ответ дан 13 июн 2013 в 9:02
3,454 12 12 серебряных знаков 9 9 бронзовых знаков

Оператор new вовсе не обязан по стандарту использовать malloc (он может, например, запросить прямо у системы без посредничества malloc ).

Деструкторы (C++)

Деструктор — это функция-член, которая вызывается автоматически, когда объект выходит из область или явно уничтожается вызовом delete или delete[] . Деструктор имеет то же имя, что и класс и предшествует тильде ( ~ ). Например, деструктор для класса String объявляется следующим образом: ~String() .

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

Рассмотрим следующее объявление класса String :

// spec1_destructors.cpp #include // strlen() class String < public: String(const char* ch); // Declare the constructor ~String(); // Declare the destructor private: char* _text; >; // Define the constructor String::String(const char* ch) < size_t sizeOfText = strlen(ch) + 1; // +1 to account for trailing NULL // Dynamically allocate the correct amount of memory. _text = new char[sizeOfText]; // If the allocation succeeds, copy the initialization string. if (_text) < strcpy_s(_text, sizeOfText, ch); >> // Define the destructor. String::~String() < // Deallocate the memory that was previously reserved for the string. delete[] _text; >int main()

В предыдущем примере деструктор String::~String использует delete[] оператор для освобождения места динамически выделенного для текстового хранилища.

Объявление деструкторов

Деструкторы — это функции с тем же именем, что и класс, но с добавленным в начало знаком тильды ( ~ ).

При объявлении деструкторов действуют несколько правил. Деструкторы:

  • Не принимать аргументы.
  • Не возвращайте значение (или void ).
  • Невозможно объявить как const , volatile или static . Однако их можно вызвать для уничтожения объектов, объявленных как const , volatile или static .
  • Можно объявить как virtual . С помощью виртуальных деструкторов можно уничтожить объекты, не зная их тип, — правильный деструктор для объекта вызывается с помощью механизма виртуальной функции. Деструкторы также можно объявить как чистые виртуальные функции для абстрактных классов.

Использование деструкторов

Деструкторы вызываются, когда происходит одно из следующих событий:

  • Локальный (автоматический) объект с областью видимости блока выходит за пределы области видимости.
  • Используется delete для освобождения объекта, выделенного с помощью new . Использование delete[] результатов неопределенного поведения.
  • Используется delete[] для освобождения объекта, выделенного с помощью new[] . Использование delete результатов неопределенного поведения.
  • Время существования временного объекта заканчивается.
  • Программа заканчивается, глобальные или статические объекты продолжают существовать.
  • Деструктор явно вызываться с использованием полного имени функции деструктора.

Деструкторы могут свободно вызывать функции-члена класса и осуществлять доступ к данным членов класса.

Существует два ограничения на использование деструкторов:

  • Вы не можете взять свой адрес.
  • Производные классы не наследуют деструктор базового класса.

Порядок уничтожения

Когда объект выходит за пределы области или удаляется, последовательность событий при его полном уничтожении выглядит следующим образом:

  1. Вызывается деструктор класса, и выполняется тело функции деструктора.
  2. Деструкторы для объектов нестатических членов вызываются в порядке, обратном порядку их появления в объявлении класса. Необязательный список инициализации элементов, используемый в строительстве этих элементов, не влияет на порядок строительства или уничтожения.
  3. Деструкторы для не виртуальных базовых классов вызываются в обратном порядке объявления.
  4. Деструкторы для виртуальных базовых классов вызываются в порядке, обратном порядку их объявления.
// order_of_destruction.cpp #include struct A1 < virtual ~A1() < printf("A1 dtor\n"); >>; struct A2 : A1 < virtual ~A2() < printf("A2 dtor\n"); >>; struct A3 : A2 < virtual ~A3() < printf("A3 dtor\n"); >>; struct B1 < ~B1() < printf("B1 dtor\n"); >>; struct B2 : B1 < ~B2() < printf("B2 dtor\n"); >>; struct B3 : B2 < ~B3() < printf("B3 dtor\n"); >>; int main()
A3 dtor A2 dtor A1 dtor B1 dtor B3 dtor B2 dtor B1 dtor 

Виртуальные базовые классы

Деструкторы для виртуальных базовых классов вызываются в порядке, обратном их указанию в направленном ациклическом графе (в глубину, слева направо, обход в обратном порядке). На следующем рисунке представлен граф наследования.

Пять классов, помеченные A до E, упорядочены в графе наследования. Класс E является базовым классом B, C и D. КлассЫ C и D являются базовым классом A и B.

Ниже перечислены определения классов для классов, показанных на рисунке:

class A <>; class B <>; class C : virtual public A, virtual public B <>; class D : virtual public A, virtual public B <>; class E : public C, public D, virtual public B <>; 

Чтобы определить порядок удаления виртуальных базовых классов объекта типа E , компилятор выполняет сборку списка, применяя следующий алгоритм.

  1. Просмотрите левую часть графа, начиная с самой глубокой точки графа (в данном случае E ).
  2. Просматривайте граф справа налево, пока не будут пройдены все узлы. Запомните имя текущего узла.
  3. Пересмотрите предыдущий узел (вниз и вправо), чтобы определить, является ли рассматриваемый узел виртуальным базовым классом.
  4. Если рассматриваемый узел является виртуальным базовым классом, просмотрите список, чтобы проверить, был ли он введен ранее. Если он не является виртуальным базовым классом, игнорируйте его.
  5. Если запоминаемый узел еще не находится в списке, добавьте его в нижней части списка.
  6. Просмотрите граф вверх и вдоль следующего пути вправо.
  7. Перейдите к шагу 2.
  8. Если путь последний путь вверх исчерпан, запомните имя текущего узла.
  9. Перейдите к шагу 3.
  10. Выполняйте этот процесс, пока нижний узел снова не станет текущим узлом.

Таким образом, для класса E порядок удаления будет следующим.

  1. Не-виртуальный базовый класс E .
  2. Не-виртуальный базовый класс D .
  3. Не-виртуальный базовый класс C .
  4. Виртуальный базовый класс B .
  5. Виртуальный базовый класс A .

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

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

Такие взаимозависимости между классами в графе наследования опасны, поскольку классы, наследуемые впоследствии, могут изменить крайний левый путь, тем самым изменив порядок построения и удаления.

Не виртуальные базовые классы

Деструкторы для не виртуальных базовых классов вызываются в обратном порядке, в котором объявляются имена базовых классов. Рассмотрим следующее объявление класса.

class MultInherit : public Base1, public Base2 . 

В предыдущем примере деструктор Base2 вызывается перед деструктором Base1 .

Явные вызовы деструктора

Редко возникает необходимость в явном вызове деструктора. Однако может быть полезно выполнить удаление объектов, размещенных по абсолютным адресам. Эти объекты обычно выделяются с помощью определяемого пользователем new оператора, который принимает аргумент размещения. Оператор delete не может освободить эту память, так как он не выделяется из свободного хранилища (дополнительные сведения см. в разделе «Новые и удаленные операторы»). Вызов деструктора, однако, может выполнить соответствующую очистку. Для явного вызова деструктора для объекта ( s ) класса String воспользуйтесь одним из следующих операторов.

s.String::~String(); // non-virtual call ps->String::~String(); // non-virtual call s.~String(); // Virtual call ps->~String(); // Virtual call 

Нотация для явных вызовов деструкторов, показанная в предыдущем примере, может использоваться независимо от того, определяет ли тип деструктор. Это позволяет выполнять такие явные вызовы, не зная, определен ли деструктор для типа. Явный вызов деструктора, если ни один из них не определен, не имеет никакого эффекта.

Отказоустойчивость

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

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

В следующем примере неявно созданный конструктор копирования сделает указатели str1.text и str2.text ссылается на ту же память, и при возврате из copy_strings() нее память будет удалена дважды, что является неопределенным поведением:

void copy_strings() < String str1("I have a sense of impending disaster. "); String str2 = str1; // str1.text and str2.text now refer to the same object >// delete[] _text; deallocates the same memory twice // undefined behavior 

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *