C++
Управление памятью
Поиск…
Синтаксис
- : :( opt ) new ( выражение-list ) ( opt ) new-initial-initial -initial-initializer ( opt )
- : :( opt ) new ( expression-list ) ( opt ) ( type-id ) new-initializer ( opt )
- : :( opt ) delete cast-expression
- : :( opt ) delete [] cast-expression
- std :: unique_ptr < type-id > var_name (новый тип-идентификатор ( opt )); // C ++ 11
- std :: shared_ptr < type-id > var_name (новый тип-идентификатор ( opt )); // C ++ 11
- std :: shared_ptr < type-id > var_name = std :: make_shared < type-id > ( opt ); // C ++ 11
- std :: unique_ptr < type-id > var_name = std :: make_unique < type-id > ( opt ); // C ++ 14
замечания
Ведущий ::
заставляет новый или удаляемый оператор искать глобальную область, переопределяя любые перегруженные новые или удаленные операторы класса.
Необязательные аргументы, следующие за new
ключевым словом, обычно используются для вызова нового места размещения , но могут также использоваться для передачи дополнительной информации в распределитель, например тега, запрашивающего выделение памяти из выбранного пула.
Выделенный тип обычно явно указан, например, new Foo
, но также может быть записан как auto
(начиная с C ++ 11) или decltype(auto)
(начиная с C ++ 14), чтобы вывести его из инициализатора.
Инициализация выделенного объекта происходит по тем же правилам, что и инициализация локальных переменных. В частности, объект будет инициализирован по умолчанию, если инициализатор iso опущен, а при динамическом распределении скалярного типа или массива скалярного типа нет гарантии, что память будет обнулена.
Объект массива, созданный новым выражением, должен быть уничтожен с помощью delete[]
, независимо от того, было ли новое выражение написано с помощью []
или нет. Например:
using IA = int[4];
int* pIA = new IA;
delete[] pIA; // right
// delete pIA; // wrong
стек
Стек представляет собой небольшую область памяти, в которую временные значения помещаются во время выполнения. Выделение данных в стек очень быстро по сравнению с распределением кучи, так как все память уже назначена для этой цели.
int main() {
int a = 0; //Stored on the stack
return a;
}
Стек названа так, потому что цепочки вызовов функций будут иметь временную память, расположенную друг над другом, каждая из которых использует отдельный небольшой раздел памяти.
float bar() {
//f will be placed on the stack after anything else
float f = 2;
return f;
}
double foo() {
//d will be placed just after anything within main()
double d = bar();
return d;
}
int main() {
//The stack has no user variables stored in it until foo() is called
return (int)foo();
}
Данные, хранящиеся в стеке, действительны только до тех пор, пока область, которая распределяет переменную, все еще активна.
int* pA = nullptr;
void foo() {
int b = *pA;
pA = &b;
}
int main() {
int a = 5;
pA = &a;
foo();
//Undefined behavior, the value pointed to by pA is no longer in scope
a = *pA;
}
Свободное хранение (куча, динамическое распределение ...)
Термин «куча» представляет собой общий вычислительный термин, означающий область памяти, из которой части могут быть выделены и освобождены независимо от памяти, предоставленной стеком .
В C++
Стандарт относится к этой области как к свободному магазину, который считается более точным.
Области памяти, выделенные из Free Store, могут жить дольше, чем исходная область, в которой она была выделена. Данные, слишком большие для хранения в стеке, также могут быть выделены из Free Store .
Сырая память может быть выделена и освобождена новыми и удаляемыми ключевыми словами.
float *foo = nullptr;
{
*foo = new float; // Allocates memory for a float
float bar; // Stack allocated
} // End lifetime of bar, while foo still alive
delete foo; // Deletes the memory for the float at pF, invalidating the pointer
foo = nullptr; // Setting the pointer to nullptr after delete is often considered good practice
Также можно назначить массивы с фиксированным размером с новыми и удалить , с немного отличающимся синтаксисом. Выделение массива несовместимо с распределением без массива, и смешивание двух приведет к повреждению кучи. Выделение массива также выделяет память для отслеживания размера массива для последующего удаления определенным образом.
// Allocates memory for an array of 256 ints
int *foo = new int[256];
// Deletes an array of 256 ints at foo
delete[] foo;
При использовании new и delete вместо malloc и free , конструктор и деструктор будут выполняться (подобно объектам на основе стека). Поэтому новые и удалить, предпочитаемые над таНосом и бесплатно.
struct ComplexType {
int a = 0;
ComplexType() { std::cout << "Ctor" << std::endl; }
~ComplexType() { std::cout << "Dtor" << std::endl; }
};
// Allocates memory for a ComplexType, and calls its constructor
ComplexType *foo = new ComplexType();
//Calls the destructor for ComplexType() and deletes memory for a Complextype at pC
delete foo;
Начиная с C ++ 11, для указания владельца рекомендуется использовать интеллектуальные указатели .
C ++ 14 добавил std::make_unique
в STL, изменив рекомендацию в пользу std::make_unique
или std::make_shared
вместо того, чтобы использовать голый новый и удалить .
Размещение новых
Бывают ситуации, когда мы не хотим полагаться на Free Store для выделения памяти, и мы хотим использовать пользовательские выделения памяти, используя new
.
В этих ситуациях мы можем использовать Placement New
, где мы можем сказать оператору «new» выделить память из предварительно выделенной ячейки памяти
Например
int a4byteInteger;
char *a4byteChar = new (&a4byteInteger) char[4];
В этом примере память, a4byteChar
представляет собой 4 байта, выделенных для «стека» с помощью целочисленной переменной a4byteInteger
.
Преимущество такого распределения памяти заключается в том, что программисты контролируют распределение. В приведенном выше примере, поскольку a4byteInteger
выделяется в стеке, нам не нужно делать явный вызов «delete a4byteChar».
Такое же поведение может быть достигнуто и для динамически распределенной памяти. Например
int *a8byteDynamicInteger = new int[2];
char *a8byteChar = new (a8byteDynamicInteger) char[8];
В этом случае указатель на память a8byteChar
будет ссылаться на динамическую память, выделенную a8byteDynamicInteger
. В этом случае, однако, нам нужно явно вызвать delete a8byteDynamicInteger
для освобождения памяти
Другой пример для класса C ++
struct ComplexType {
int a;
ComplexType() : a(0) {}
~ComplexType() {}
};
int main() {
char* dynArray = new char[256];
//Calls ComplexType's constructor to initialize memory as a ComplexType
new((void*)dynArray) ComplexType();
//Clean up memory once we're done
reinterpret_cast<ComplexType*>(dynArray)->~ComplexType();
delete[] dynArray;
//Stack memory can also be used with placement new
alignas(ComplexType) char localArray[256]; //alignas() available since C++11
new((void*)localArray) ComplexType();
//Only need to call the destructor for stack memory
reinterpret_cast<ComplexType*>(localArray)->~ComplexType();
return 0;
}