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

Являются ли новые или удаленные перезагрузки элементов в производном классе полезными?

Я просто отвечал на вопрос об отсутствии места размещения, который соответствует новому месту размещения. Причина, по-видимому, заключается в том, что метод operator delete вызывается в соответствии с динамическим типом объекта (соответствующий типу, используемому для поиска operator new).

Размещение new было полезно для меня. Когда речь заходит о пользовательском распределении, вероятно, существует класс повторного использования, в котором разные экземпляры управляют разными пулами. Синглтоны - это анти-шаблон и все такое.

Я понимаю удобство работы new thing;, не отслеживая распределитель, но выполнение вещей для разных ветвей иерархии типов кажется довольно запутанным.

Существует ли реальный сценарий, в котором производный класс использует другой базовый блок распределения и полагается на виртуальный деструктор, чтобы найти правильный член operator delete?

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

4b9b3361

Ответ 1

Я действительно использовал это в прошлом! Это полезно для неравномерных архитектур памяти - платформ с очень маленькими очень быстрыми областями памяти без ОС и т.д.

В частности, представление, например. ARM-чип с небольшим количеством TCM (плотно связанная память, по существу, SRAM, встроенная в SoC, например, с 1-тактным временем доступа).

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

Простой член operator new, который использует этот TCM для только производных классов, теперь может иметь смысл: мы не можем позволить себе иметь всю иерархию классов с использованием этой SRAM, но для некоторого количества данных с низкой инстанцированием, используемых производных классов, он становится простой и эффективной оптимизацией. Мы вернулись на 2% -10% или более от времени кадра в нескольких случаях, перенаправляя определенные распределения таким образом.

Ответ 2

Я никогда не использовал новые/удаленные перегрузки в производном классе, или я когда-либо думал об этом, но этот вопрос был интересен, и я решил провести некоторое исследование и дать ему шанс. Я нашел пару подходящих ссылок:

ARM http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14267.html

В этой ссылке есть пример кода производного класса, перегружающего его базовый класс перегруженным новым.

Университет Вандербильта http://www.vuse.vanderbilt.edu/~adamsja/Courses/CS251/Projects/4/memPool.pdf

В этой ссылке не содержится каких-либо существенных сведений о перегрузке нового в производном классе, однако в нем упоминаются некоторые интересные причины, почему для перегрузки новых. Эти причины включают:

  • Чтобы компенсировать субоптимальное выравнивание в распределителе по умолчанию
  • Чтобы связать связанные объекты рядом друг с другом
  • Чтобы получить нетрадиционное поведение, такое как перезапись памяти нулями для повышения безопасности данных приложения.

Теперь, основываясь на этих двух ссылках, я решил, что может быть несколько причин перегрузки new/delete в производном классе. Мои причины в основном согласуются с причинами, которые я перечислял из презентации VU, но также кажутся релевантными на основе ссылки ARM, которая подразумевает мне встроенный или специализированный сценарий.

  • Встраиваемые системы часто очень обычны на аппаратном уровне, однако они распространены в стандартизованном смысле на уровне программного обеспечения. Я мог видеть ситуацию, когда в зависимости от настройки конфигурации/аппаратного ремня, что во время выполнения выбирается определенный тип объекта (производный класс), потому что ему необходимо выделять память определенным образом или определенным местом.
  • Кластеризация кажется правдоподобной, потому что я мог представить себе сценарий, когда некоторые объекты обрабатываются одинаково в "рабочем процессе" высокого уровня, но при обработке разные типы этих объектов обрабатываются по-разному. Возможно, было бы полезно найти объекты определенного типа рядом друг с другом для задач с интенсивным процессом (т.е. Поиска, сортировки, определения приоритетов и т.д.).
  • Последняя точка интересна тем, что я вижу некоторые случаи, когда распределение зависит от классификации безопасности хранимой информации.

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

Ответ 3

Существует ли реальный сценарий, в котором производный класс использует другой базовый блок распределения и полагается на виртуальный деструктор, чтобы найти правильный член operator delete?

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

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

Я не могу дать вам конкретный пример этого, потому что за эти годы я обнаружил, что избегать выделения/освобождения окупается быстрее, чем ускорить его, поэтому почти за двадцать лет я редко перегружал класс- специфический new/delete. (Конечно, как обычно, когда мы говорим о сложной оптимизации, появляется "игра", поэтому мы можем представить себе игру, которая должна создавать и уничтожать огромное количество очень разных объектов.)

Ответ 4

Элемент operator new/delete может быть полезен для удаления нежелательного пользовательского выделения из базового класса, например, если база не предназначена для расширения. Но базе нужен либо виртуальный деструктор, либо базовый объект никогда не должен быть объектом delete.

Собственно, отправка виртуального деструктора operator delete требуется независимо от перегрузки. Вероятно, функция поддерживает множественное наследование, и динамическое обнаружение правильного члена operator delete является лишь побочным эффектом.

Правило просто состоит в том, что виртуальный деструктор для самого производного объекта вызывает operator delete. Основная причина заключается в том, чтобы передать правильный наиболее производный указатель и размер. То, что контекст также позволяет программе динамически находить правильную функцию освобождения, является просто счастливым побочным эффектом.

Таким образом, может быть или не быть полезным приложением для динамической отправки delete, но для его работы не существует специального механизма.

Простая демонстрация:

struct pad {
    int x;

    virtual ~pad() {}
};

struct b {
    int x;
};

struct vb {
    int x;

    virtual ~vb() {}
};

struct d : pad, b, vb {};

void operator delete( void *p ) {
    std::cout << "free " << p << '\n';
}

int main() {
    std::cout << "With virtual destructor:\n";
    d *p = new d;
    std::cout << "allocate " << p << ", delete " << static_cast< vb * >( p ) << '\n';
    delete static_cast< vb * >( p );

    std::cout << "With plain destructor:\n";
    p = new d;
    std::cout << "allocate " << p << ", delete " << static_cast< b * >( p ) << '\n';
    delete static_cast< b * >( p );
}