В языке C++ есть обычный оператор new, а есть глобальная версия оператора 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 в случае, если память не была выделена а так же гарантирует что при возникновении ошибки выделения, указатель будет содержать нуливое значение.