Как удаление размещения C ++ работает внутри (время выполнения C ++)? Как преодолеть его недостатки? - PullRequest
0 голосов
/ 31 мая 2019

C ++ является асимметричным размещения новых и удаления размещения.Нам разрешено перегружать размещение новых практически произвольными способами.Однако функции удаления размещения только вызываются из новых выражений размещения.В частности, они вызываются, если конструктор объекта выдает исключение.Нет никакого способа вызвать удаление размещения для кода приложения вообще.

У меня есть следующие недоразумения и вопросы, которые необходимо уточнить:

1) Почему компилятор C ++ не может просто отклонить сигнатуру нового метода размещения размещенияесли не определено место размещения, удалить дубликат?Это может помочь убить возможности утечки памяти в этом контексте.

2) Если у меня есть несколько пулов памяти (управляемых кодом приложения), и я хочу другое размещение, новое для выделения памяти из разных пулов, просто нетспособ поддержать это из-за того, что нет никакого способа узнать, из какого пула памяти пришел указатель в операторе delete?(оператор удаления имеет только void * info).Есть ли способ, которым я могу сделать это в C ++?

struct Node {
    void* operator new(size_t size, Strategy s) {
        // Depend on different strategy, allocate memory
        // from different Memory pool
    }

    void operator delete(void* memory, Strategy s) {
        // Return the memory to different Memory pool
        // depends on the Strategy
        // However this delete will only be invoked by
        // c++ runtime if Node constructor throws.
    }

    void operator delete(void* memory, size_t s) {
        // This delete doesn't have any clue about the
        // the strategy. Hence, it can't decide which
        // Memory pool to return to.
    }
}

3) В контексте размещения new среда выполнения C ++ будет вызывать размещение delete с тем же аргументом.Как среда выполнения C ++ достигает этого?

Ответы [ 2 ]

2 голосов
/ 31 мая 2019

В этом ответе предполагается, что вопрос относится к пользовательским функциям распределения мест размещения :

void* operator new  ( std::size_t count, user-defined-args... );
void* operator new[]( std::size_t count, user-defined-args... );

и пользовательские функции освобождения места размещения :

void operator delete  ( void* ptr, args... );
void operator delete[]( void* ptr, args... );

Поведение этих функций:

  • operator new: если определено, вызывается пользовательским размещением одного объекта новым выражением с соответствующей подписью. Если версия класса определена, она вызывается в предпочтении к этому. Если ни то, ни другое не предоставлено пользователем, выражение нового размещения некорректно сформировано.
  • operator new[]: то же самое, но для формы массива.
  • operator delete: если определено, вызывается пользовательским размещением одного объекта новым выражением с соответствующей подписью, если конструктор объекта выдает исключение Если версия класса определена, она вызывается в предпочтении к этому. Если ни один из них не предоставлен пользователем, функция освобождения не вызывается.
  • operator delete[]: то же самое, но для формы массива.

Чтобы ответить на ваши вопросы:

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

  2. Вы должны будете включить механизм, с помощью которого значение удаляемого указателя void * может использоваться для определения того, в каком пуле памяти он находится. Это должно быть возможно, потому что ваши разные пулы не могут вернуть одинаковое значение одновременно. Наивным методом может быть то, чтобы каждый пул управлял непересекающимся диапазоном адресов и проверял указатель на диапазон адресов каждого пула. Или, возможно, вы могли бы хранить метаданные в памяти перед указанным местоположением, например, версия new будет выделять N + 16 байт, сохранять метаданные в первых 16 и возвращать пользователю указатель на 16-й байт блока). Или вы можете сохранить структуру данных, связывающую каждый активный указатель с метаданными.

  3. Оценка new-выражения для выделения объекта типа класса будет выглядеть примерно так:

    • Позвоните operator new, передав аргументы
    • Предполагая, что это успешно, вызовите конструктор класса (если это тип класса выделяется).
    • Если конструктор класса выбрасывает и существует соответствие operator delete, вызовите эту функцию, передав аргументы.
    • Теперь оценка нового выражения завершена, и управление возвращается либо к коду, содержащему новое выражение, либо к механизму обработки исключений.
1 голос
/ 31 мая 2019

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

Если бы это были new с for for void* у нас было бы следующее уничтожение:

template<class T> void destroy (Strategy s, T* ptr)
{
    ptr->~T();
    // return to custom heap
 }

Вы могли бы использовать причудливые трюки, чтобы избавиться от стратегии, например, проверить, что находится в куче ptr, или сохранить его с отрицательным смещением в new.

Бесполезный исторический ответ:

Прошло много времени, но я помню синтаксис:

delete (strategy) pointer;

Похоже, что это бессмысленная ерунда от BC4.5.

...