Подтвердить что ты не робот

Как я мог бы разумно перегрузить оператора размещения?

С++ позволяет перегружать operator new - как глобальный, так и для каждого класса - обычный operator new, operator new[], используемый с оператором new[] и размещением operator new отдельно.

Первые два из этих трех обычно перегружены для использования настраиваемых распределителей и добавления трассировки. Но размещение operator new кажется довольно простым - оно фактически ничего не делает внутри. Например, в Visual С++ реализация по умолчанию просто возвращает адрес, переданный в вызов:

//from new.h
inline void* operator new( size_t, void* where )
{
   return where;
}

Что еще можно было сделать? Почему и как я мог разумно перегрузить размещение operator new?

4b9b3361

Ответ 1

Правильный ответ вы не можете заменить размещение оператора новым.

§18.4. 1.3 Формы размещения
Эти функции зарезервированы, программа на С++ не может определять функции, которые заменяют версии в стандартной библиотеке С++.

Обоснование: единственной целью операторов распределения и освобождения является выделение и освобождение памяти, поэтому при предоставлении памяти больше ничего не должно быть сделано. (В стандарте конкретно отмечается, что эти функции "Намеренно не выполняют никаких других действий".)

Ответ 2

Технически, размещение operator new - это любой operator new, который принимает дополнительные аргументы, кроме размера необходимой памяти.

Итак, new(std::nothrow) X использует размещение operator new, и поэтому new(__FILE__, __LINE__) X.

Единственная причина для переопределения operator new(size_t, void*) может заключаться в добавлении информации трассировки, но я думаю, что потребность в ней будет довольно низкой.

Ответ 3

Один пример находится в разделе Часто задаваемые вопросы по Stroustrup.

Ответ 4

Наиболее очевидным переопределением будет копирование этой реализации.

Другим разумным будет добавление некоторых проверок (например, проверка отсутствия "связанного маркера" в зоне запроса).

Я думаю, однако, что смысл больше, чем вы должны переопределить его, как только вы переопределите остальных (для данного класса), из-за механики имени посмотрите (или не переопределите его, чтобы предотвратить его использование, это тоже хорошо, но это осознанное решение).

Ответ 5

Чтобы определить ваше собственное управление памятью для предварительно определенной области, приятно использовать.

Чтобы иметь разные представления по тем же физическим данным (нет необходимости переместить данные), это другое межсетевое использование. Он также позволяет вам читать структурированный файл в виде символов в буфере, а затем наложение их логической структуры путем определения объекта этого класса над буфером. Комбинация этой вещи с отображением файлов в файлах может обеспечить большие улучшения в производительности. Аппаратное обеспечение с отображением памяти... Итак, тысячи приложений!

Ответ 6

Я видел пример, когда два аргумента new [] были перезаписаны для возврата блоков памяти, предварительно заполненных с помощью char, переданных в качестве дополнительного аргумента. Я не помню, что использовал исходный код (возможно, memset()), но это было функционально примерно так:

#include <iostream>
#include <algorithm>
#include <new>
void* operator new [](size_t n, char c)
{
        char* p = new char[n];
        std::fill(p, p+n, c);
        return p;
}
int main()
{
        char* p = new('a') char[10];
        std::cout << p[0] << p[1] << ".." << p[9] << '\n';
}

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

Ответ 7

Я не совсем уверен в вопросе, но следующее переопределяет размещение нового на уровне класса:

struct Bar {
void* operator new(size_t /* ignored */, void* where) throw() { return where; }
};

int main() {
  char mem[1];
  Bar* bar = new(mem) Bar;
}

Я считаю, что это законный С++ (и компилируется и работает отлично с gcc 4.4.6).

Вы можете изменить реализацию этого оператора по своему усмотрению (включая удаление предложения throw(), что будет означать, что компилятор больше не проверяет указатель where для null перед вызовом конструктора). Будьте осторожны, однако.

§ 18.4. 1.3 интересен. Я считаю, что это справедливо для новой функции глобального оператора, а не для конкретных классов.

Ответ 8

Наиболее важной дополнительной функциональностью для размещения новой перегрузки будет проверка выравнивания адреса.

Например, допустим, что для некоторого класса требуется выравнивание по 16 байт. Разработчик перегружает новые, новые [], удаляет и удаляет [] - чтобы убедиться, что все правильно выровнено.

Все работает отлично до того момента, когда он пытается использовать свой класс с библиотекой, которая использует новое размещение... Библиотека не имеет понятия, требуется ли что-то выравнивание для класса и адрес, который он пытается "разместить" объект, чтобы он мог не выравниваться - большая стрела.

Простейший пример такой ситуации - попробуйте использовать std::vector <T> где T требует нестандартного выравнивания.

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

Ответ 9

Мое основное использование - создать большой массив объектов. Его выполнение намного лучше и имеет меньшие накладные расходы для распределения памяти в целом блоке, то есть с использованием VirtualAlloc из Win32 (при программировании окон). Затем вы просто передаете ptr внутри этого блока для каждого нового места размещения объектов, например:

char *cp = new char[totalSize];

for(i = 0; i < count; i++, cp += ObjSize)        
{                                                        
    myClass *obj = new(cp) myClass;             
}