MyTetra Share
Делитесь знаниями!
Как в C++ пользоваться глобальным, локальным и размещающим оператором new для работы с памятью
Время создания: 09.10.2024 12:58
Автор: Xintrea
Текстовые метки: c++, си++, глобальный, оператор, new, delete, placement, выделение, память, аллокация, аллокатор, синтаксис
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/172846789557c6kbovav/text.html на raw.github.com

В языке C++ есть обычный оператор new, есть глобальная версия оператора new., а так же есть размещающая версия оператора new. И это разные вещи:



  • Обычный (локальный) оператор new - выделяет память и размещает в ней объект;
  • Глобальный оператор new - выделяет память под объект, но не размещает в ней сам объект;
  • Размещающий оператор new - наоборот, не выделяет память, но принимает указатель на выделенную память и в ней размещает объект. В английской литературе обозначается либо полным термином: non-allocating placement new, либо сокращенным: placement new.



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


Глобальная версия оператора new выделяет заданное в его параметре количество памяти (количество байт) без вызова конструктора для объекта (потому что она применяется не к объекту). После этого происходит просто возврат указателя на выделенную память.


Важно понимать взаимосвязь между обычным new и глобальным new:



Как работает обычный new в C++?


1. Компилятор вызывает глобальный оператор new для выделения памяти под объект. Например:


::operator new(sizeof(MyClass)).


2а. В этой выделенной памяти происходит инициализация объекта (например, отрабатывает список инициализации, принадлежащий конструктору).


2б. Компилятор автоматически вызывает конструктор класса, чтобы инициализировать объект.


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



Сигнатура глобального оператора new:



void* operator new(std::size_t size);



Глобальный оператор new выделяет блок памяти размером size байт. Поэтому он возвращает тип void*, который всегда можно преобразовать к указателю на любой заданный тип.


В случае неудачи оператор new (любой версии) выбрасывает исключение std::bad_alloc (такое поведение сделано начиная со стандарта C++98, а ранее new возвращал пустой указатель, см. далее).


Классический пример использования:



void* buffer = ::operator new(numBytes);



Когда используется глобальный оператор new:



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



Вот пример выделения памяти под объект без его инициализации:



// Выделение памяти без вызова конструктора

void* rawMemory = ::operator new(sizeof(MyClass));


// Приведение указателя на выделенную память к типу MyClass

MyClass* myObject = static_cast<MyClass*>(rawMemory);


// Вызов методов невозможен, пока не вызван конструктор


// Ручной вызов конструктора через placement new

new (myObject) MyClass;


// Теперь можно использовать объект, так как конструктор был вызван

myObject->sayHello();


// Явный вызов деструктора, так как память освобождается вручную

myObject->~MyClass();


// Освобождение памяти без вызова деструктора

::operator delete(rawMemory);



Здесь появляется еще один вид оператора new: "placement new". Placement new — это особая форма оператора new, которая позволяет разместить объект в заранее выделенной области памяти. В отличие от стандартного оператора new, который сначала выделяет память, а затем вызывает конструктор, placement new только вызывает конструктор объекта, используя уже выделенную память.


Синтаксис placement new:



new (pointer) Type(arguments);



где:


  • pointer — это указатель на заранее выделенную область памяти, в которой будет размещен объект.
  • Type — это тип объекта, который нужно создать.
  • arguments — это аргументы конструктора объекта (если таковые имеются).



Этот синтаксис не следует путать с nothrow-синтаксисом оператора new, который используется, чтобы вернуть поведение этого оператора таким, каким оно было до стандарта C++98:



int* ptr = new (std::nothrow) int[1000];


if (ptr == nullptr)

{

// Память не была выделена

}



Такой синтаксис блокирует генерацию исключения std::bad_alloc в случае, если память не была выделена а так же гарантирует что при возникновении ошибки выделения, указатель будет содержать нуливое значение.


Так же в этом разделе:
 
MyTetra Share v.0.67
Яндекс индекс цитирования