Похож operator new
и operator new[]
имейте точно ту же подпись:
void* operator new( size_t size );
void* operator new[]( size_t size );
и сделайте точно то же: любой возврат указатель на достаточно большой блок сырых данных (не инициализированный всегда) память или выдает исключение.
Также operator new
назван внутренне, когда я создаю объект с new
и operator new[]
- когда я создаю массив объектов с new[]
. Тем не менее вышеупомянутые две специальных функции вызваны C++ внутренне точно тем же способом, и я не вижу, как два вызова могут иметь различные значения.
Какова цель наличия двух различных функций с точно теми же подписями и точно тем же поведением?
В Проектировании и эволюции C ++ (раздел 10.3) Страуструп упоминает, что если новый оператор для объекта X сам использовался для выделения массива объекта X, тогда автору X :: operator new () также придется иметь дело с распределением массива, что не является обычным использованием для new () и добавляет сложности. Таким образом, не рассматривалось использование new () для размещения массива. Тогда не было простого способа выделить разные области хранения для динамических массивов. Решением было предоставить отдельные методы выделения и освобождения для массивов: new [] и delete [].
Операторы можно переопределить (для определенного класса, в пространстве имен или глобально), и это позволяет вам чтобы предоставить отдельные версии, если вы хотите обрабатывать выделение объектов иначе, чем распределение массивов. Например, вы можете выделить из разных пулов памяти.
Стандарт говорит, что new T
вызывает оператор new ()
и new T []
приводит к вызову оператора new [] ()
. Вы можете перегрузить их, если хотите. Я считаю, что по умолчанию между ними нет разницы. Стандарт говорит, что они заменяемы (3.7.3 / 2):
Библиотека предоставляет определения по умолчанию для глобальных функций распределения и освобождения. Некоторые глобальные функции выделения и освобождения заменяемы (18.4.1). Программа на C ++ должна предоставлять не более одного определения заменяемой функции выделения или освобождения. Любое такое определение функции заменяет версию по умолчанию , предоставленную в библиотеке (17.4.3.4). Следующие функции распределения и освобождения (18.4) неявно объявляются в глобальной области видимости в каждой единице перевода программы
void * operator new (std :: size_t) throw (std :: bad_alloc); void * operator new [] (std :: size_t) throw (std :: bad_alloc); оператор void delete (void *) throw (); оператор void delete [] (void *) throw ();
Я достаточно хорошо рассмотрел это, и, откровенно говоря, нет причин с точки зрения интерфейса.
Единственная возможная причина, о которой я могу думать, - это разрешить подсказку по оптимизации для реализации, оператор new []
, вероятно, будет вызван для выделения больших блоков памяти ; но это действительно очень слабое предположение, так как вы могли бы new
очень большую структуру или новый char [2]
, который на самом деле не считается большим.
Обратите внимание, что оператор new []
не добавляет никакого волшебного дополнительного хранилища для счетчика массива или чего-то еще. Задача оператора new []
- определить, сколько накладных расходов (если они есть) необходимы, и передать правильное количество байтов оператору new []
.
[Тест с gcc показывает, что new []
не требует дополнительного хранилища, если только тип создаваемых элементов массива не имеет -тривиальный десктруктор.]
С точки зрения интерфейса и контракта (кроме требования использования соответствующей соответствующей функции освобождения) оператор new
и оператор new []
идентичны.
Одна из целей состоит в том, что они могут быть отдельно определены пользователем. Итак, если я хочу инициализировать память в отдельных объектах, выделенных кучей, на 0xFEFEFEFE, а память в массивах, выделенных кучей, на 0xEFEFEFEF, потому что я думаю, что это поможет мне с отладкой, тогда я могу.
Другой вопрос, стоит ли это того.Я предполагаю, что если ваша конкретная программа в основном использует довольно маленькие объекты и довольно большие массивы, вы можете выделить разные кучи в надежде, что это уменьшит фрагментацию. Но в равной степени вы можете определить классы, которым вы выделяете большие массивы, и просто переопределить оператор new []
для этих классов. Или оператор new
может переключаться между разными кучами в зависимости от размера.
Фактически есть разница в формулировках требований. Один выделяет память, выровненную для любого объекта указанного размера, другой выделяет память, выровненную для любого массива указанного размера. Я не думаю, что есть какая-то разница - массив размером 1 наверняка имеет такое же выравнивание, что и объект, - но я могу ошибаться. Тот факт, что по умолчанию версия массива возвращает то же самое, что и версия объекта, настоятельно указывает на отсутствие разницы. Или, по крайней мере, требования к выравниванию для объекта строже, чем для массива, что я не могу понять ...