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

17

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

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

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

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

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

    
задан Potatoswatter 02.01.2013 в 11:15
источник
  • Является ли это отличным от того, почему нужно заменить стандартные новые и удалять операторы ?. На вопрос Q, заданный в заголовке, отвечает связанный ответ. Но ваша деталь Q задает еще одну конкретную Q. Оба являются довольно отчетливыми Q. –  Alok Save 02.01.2013 в 11:17
  • @AlokSave По умолчанию они отличаются от членов, поэтому да. Речь идет о конкретном случае, когда специальный оператор удаляет, а не по умолчанию, выбирается виртуальным деструктором. –  Potatoswatter 02.01.2013 в 11:18
  • @AlokSave О, я вижу. Поскольку этот ответ (и другие там) немного длинный, может быть, вы могли бы скопировать части, имеющие отношение к этому Q? –  Potatoswatter 02.01.2013 в 11:20
  • Те же причины хорошо подходят для перегрузок членов. Нет? Угадайте, что вы видели его после вашего первого комментария, и пока я писал этот комментарий. Этот комментарий должен уйти. Он когда-нибудь появится. –  Alok Save 02.01.2013 в 11:20
  • @AlokSave Ну, нет. Переопределение глобального означает получение нового / лучшего алгоритма общего распределения. Перегрузка определенных классов означает, что разные классы делают разные вещи, возможно, переходя к глобальным. Я спрашиваю о случае, когда производный класс делает что-то отличное от его базы, что еще более специфично, но имеет специализированные семантические механизмы в спецификации языка. –  Potatoswatter 02.01.2013 в 11:22
Показать остальные комментарии

4 ответа

7

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

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

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

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

    
ответ дан leander 04.05.2013 в 20:15
  • Как вы справились с удалением? OP спрашивает, нужен ли вам специальный метод удаления для использования со специальным новым методом. –  Old Pro 07.05.2013 в 09:21
  • Прошло некоторое время с тех пор, как мы использовали это в иерархиях классов, а не в больших менеджерах singleton-esque без каких-либо виртуальных машин, но в тех случаях Object это была простая перезагрузка элемента, а delete вызывается родителем в сценарии. В этом случае корень иерархии типа объекта имел виртуальный деструктор. Несмотря на отвращение к нашей монолитной объектной базе и сценографу, она хорошо работала для этих случаев. –  leander 07.05.2013 в 15:06
5

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

ARM Ссылка

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

Университет Вандербильта Ссылка

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

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

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

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

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

    
ответ дан flukey 02.05.2013 в 23:31
  • +1 для работы с ногами и нескольких причин. Я рассматривал это для пула объектов, но мы в конечном итоге висели от глобальной новой перегрузки. –  leander 07.05.2013 в 15:01
1
  

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

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

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

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

    
ответ дан sbi 02.05.2013 в 20:07
  • Может быть. Оптимизация вызовов виртуальных функций была бы еще одной очевидной оптимизацией в таких условиях. –  Potatoswatter 03.05.2013 в 03:47
  • @Potatoswatter: Да, конечно. Однако я был явно нацелен на необходимость оптимизации распределения. –  sbi 03.05.2013 в 15:16
  • Это не определено, хотя это не соответствует стандарту C ++! лол –  Alec Teal 24.03.2014 в 16:53
0

Член 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 );
}
    
ответ дан Potatoswatter 03.05.2013 в 04:26