LINUX.ORG.RU

make_unique vs new

 ,


0

3

Добрый день. Подскажите пожалуйста, в чем преимущество make_unique перед просто созданием unique_ptr. Как я понял одно из преимуществ это отсутствие утечек при make_unique и где-то еще слышал про проблемы фрагментации. Кто сталкивался с этими, можете пояснить пожалуйста?


про проблемы фрагментации

Это в shared_ptr при создании объекта через make_shared память и под внутренние структуры умного указателя и под сам объект выделяются одним куском.

unique_ptr никаких дополнительных затрат по памяти не несёт.

ox55ff ★★★★★
()

Не знаю кто там как, а я - обязательно грепаю чужой код на наличие new/delete, если нашел, то говнокодер детектед с соответсвущими выводами (если там не placement new). Также со всякими reinterpret_cast трюками, тоже грепаю, любой каст не в char* с большой вероятностью - говнокод.

kvpfs ★★
()

Есть еще одна особенность в плане использования. make_unique может определять типа аргумента с момента своего появления. А вот создание unique_ptr с помощью конструктора умеет определять тип аргумента только с C++17. Соответственно до C++17 использование unique_ptr было компактнее.

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от Usruser

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

da17
() автор топика
Последнее исправление: da17 (всего исправлений: 1)

Подскажите пожалуйста, в чем преимущество make_unique перед просто созданием unique_ptr.

Для unique_ptr разницы нет. unique_ptr удаляет объект на который он ссылается (если он не NULL) при удалении самого unique_ptr. Для unique_ptr можно извлечь хранимый указатель через release() и передать в код, который использует обычные указатели и delete.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от kvpfs

любой каст не в char* с большой вероятностью - говнокод.

Слишком категорично. Ну вот я читаю бинарный файл. Знаю, что в начале есть определённый заголовок. Могу просто кастануть char*, указывающий на данные из файла, в Header* и удобно читать данные.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Ещё shared_ptr хранит дополнительный счётчик ссылок для слабых указателей что в 90% случаев не требуется.

X512 ★★★★★
()
Ответ на: комментарий от kvpfs

Не знаю кто там как, а я - обязательно грепаю чужой код на наличие new/delete, если нашел, то говнокодер детектед с соответсвущими выводами (если там не placement new). Также со всякими reinterpret_cast трюками, тоже грепаю, любой каст не в char* с большой вероятностью - говнокод.

Лол

Crocodoom ★★★★★
()
Ответ на: комментарий от X512

Вроде там уже что-то на уровне идиом типа в каждом указателе есть структура типа control block.

da17
() автор топика
Ответ на: комментарий от Crocodoom
final boolean MALE = true;
Petuh petushilo = new Petuh(MALE)

Грепаем код джавистов, говорим все что о них думаем.

da17
() автор топика
Последнее исправление: da17 (всего исправлений: 2)
Ответ на: комментарий от Crocodoom

Лол

Сишники из 70-ых не знающие про RAII в треде.

X512 ★★★★★
()
Ответ на: комментарий от ox55ff

Могу просто кастануть char*, указывающий на данные из файла, в Header* и удобно читать данные.

Любой I/O, очевидно, где-то в кишках содержит касты поинтеров

Crocodoom ★★★★★
()
Ответ на: комментарий от ox55ff

Слишком категорично. Ну вот я читаю бинарный файл. Знаю, что в начале есть определённый заголовок. Могу просто кастануть char*, указывающий на данные из файла, в Header* и удобно читать данные.

Так против данного кейса я ничего не имею, это можно. retinterpret_cast<char(singed/unsigned)/std::byte*> - практически единственные варианты, которые можно. Каст указателей за пределами данного сценария нужно смотреть под лупой, хорошо бы даже в коде оставить коммент с объяснением своих действий.

Прочитал по диагонали, нельзя так. Копировать надо, вон даже bit_cast завезли для удобства.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 2)
Ответ на: комментарий от moonmadness

new – 700, delete – 57

Тичот! Шерето!!

А ты уточнял, что это именно за new/delete?

apt_install_lrzsz ★★★
()
Ответ на: комментарий от Crocodoom

лол, не лол, но вот не один раз находил таким образом ошибки, писал авторам, правили, говорили спасибо.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Копировать надо

Ужас. А если мне бинарный файл весом 16 ГБ нужно обработать, то просить пользователя докинуть 64 ГБ, чтобы хватило на загрузку в ОЗУ и самого файла и на его файловый кэш и на вторичные куски, которые ты предлагаешь плодить копированием на каждый чих? И какая при этом будет производительность? Ну если говнокодить в стиле вебмакак, то норм.

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

bit_cast

с++20. Тут у кучи народа до сих пор таргет c++14.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Нет проблемы. Все эти трюки ничего не дадут кроме ЮБ, компилятор даже но О1 достаточно умён для того, чтобы сгенерить оптимальный код, который по скорости не будет уступать касту указателя и чтению из него. Вот всего одно чтение в регистра осталось.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Бред. Никакого отношения к «ЮБ» это не имеет, кроме того, которое имеет и bit_cast.

компилятор даже но О1 достаточно умён для того, чтобы сгенерить оптимальный код

…поэтому нужно писать заведомо менее эффективный код?

Вот всего одно чтение в регистра осталось.

Увеличь размер «Header» и возвращай не одно поле, а функцию от нескольких полей.

Siborgium ★★★★★
()
Ответ на: комментарий от ox55ff

Правильный подход это проецирование файла на виртуальную память через mmap.

Так-то оно так, но для корректной обработки ошибок mmap’нутого файла нужно писать хэндлер SIGBUS, а это дело вовсе нетривиальное. С read достаточно возвращаемого значения и errno.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Бред. Никакого отношения к «ЮБ» это не имеет

Не имеет каст указателя в черт знает что? Имеет, самое прямое.

поэтому нужно писать заведомо менее эффективный код?

Значит на его валидность можно забить?

Увеличь размер «Header» и возвращай не одно поле, а функцию от нескольких полей.

Так тоже самое будет при касте char* в Header*, тебе также придётся читать несколько полей в регистр при расчете «функции» от них. Ну или в студию пример, где прямой каст эффективнее.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Не имеет каст указателя в черт знает что? Имеет, самое прямое.

Как, по-твоему, работает bit_cast? Подсказываю: см. раздел Possible implementation https://en.cppreference.com/w/cpp/numeric/bit_cast

Ну или в студию пример, где прямой каст эффективнее.

https://godbolt.org/z/dzzYv6Kcs

Никакая копия никуда не устраняется.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

компилятор даже но О1 достаточно умён для того, чтобы сгенерить оптимальный код, который по скорости не будет уступать касту указателя и чтению из него. Вот всего одно чтение в регистра осталось.

Увеличь размер «Header» и возвращай не одно поле, а функцию от нескольких полей.

Лол, нет. Надо просто собрать разные объектники с forward declaration, а особым писком будет еще и засунуть объектники в разные исполняемые образы, компонуемые уже загрзучиком.

LamerOk ★★★★★
()
Ответ на: комментарий от Siborgium

для корректной обработки ошибок mmap’нутого файла нужно писать хэндлер SIGBUS

Зачем? Почему не достаточно проверок на диапазоны читаемых значений, которую надо делать в любом случае?

LamerOk ★★★★★
()
Ответ на: комментарий от moonmadness

Там в некторые GUI контролы передаются указатели которые потом надо вручную удалять. В new криминала в общем-то нет, а вот голого delete по возможности надо избегать.

X512 ★★★★★
()
Ответ на: комментарий от X512

Секторы битые и страницы не читаются?

Почему сразу не оперативная память? Или сбойный процессор с ошибками?

LamerOk ★★★★★
()
Ответ на: комментарий от Siborgium

Никакая копия никуда не устраняется.

Это лишь показывает, что массивы не надо копировать. Ну там ведь проблема в «умности» компилятора, он просто не справляется, сдаётся и делает копию. Но достаточно переписать так и никаких копий: https://godbolt.org/z/b59cjYhMh

Но у тебя ЮБ, а у меня - нет.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

https://godbolt.org/z/b59cjYhMh

Версию с memcpy без поллитра не разберёшь. Ухудшение читаемости - путь к ошибкам.

Ассемблерный код с копированием тяжелее за счёт собственно самого копирования: https://godbolt.org/z/13hTrPvh1 Как перепишешь?

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Здесь указатели утекают во внешнюю либу, компиль отказывается оптимизировать. Ок, рецепт мой не универсален. Но ведь и вы на ЮБ пишите, дождетесь или шальной оптимизации, или траблов с выравниванием на какой-нибудь архитектуре. Несложно сгенерить конструкцию с внешней либой, когда ваши касты поломаются.

kvpfs ★★
()
Ответ на: комментарий от ox55ff

Мы только читаем. Выстрела в ногу не будет.

Ну это странная уверенность, непонятно из чего она следует. Как пример - разыменовывание нулевого указателя на шланге с оптимизацией:

int main() {
    int *i = nullptr;
    std::cout << *i << std::endl;
}

Чтение указателя заменяется подстановкой мусора из регистра, ничего не падает.

Если компилятор сам себе докажет, что указатель получен «незаконно» в каком-нибудь кривом reinterpret_cast с последующим чтением, то он имеет полное право «наказать». Будет ли он так делать - другой вопрос, но прав будет, в этом даже резон можно найти - не приучать к плохому, на корню пресекать такие фокусы. Сделал, получил по пальцам, больше не лезешь.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

то говнокодер детектед с соответсвущими выводами

молодой шашкомахатель?

(если там не placement new)

Кроме placement new еще бывают, как минимум, случаи создания экземпляров классов с private/protected конструкторами в френдах.

eao197 ★★★★★
()
Ответ на: комментарий от ox55ff

Могу просто кастануть char*, указывающий на данные из файла, в Header* и удобно читать данные.

Формально до C++20 это UB (в C++20 включили P0593). Но до сих пор все это прокатывало. Возможно потому, что компиляторостроители позаботились о том, чтобы не ломать код тех, кто такие трюки применяет (а их невероятно много).

Подозреваю, что в C++17 от UB можно избавиться с применением std::launder, но на 100% не уверен :(

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Кроме placement new еще бывают, как минимум, случаи создания экземпляров классов с private/protected конструкторами в френдах.

Не, не аргумент, достаточно сделать один макрос:

#define CRT_SHARED_INACCESSIBLE_CTR(T, ...) \
   std::shared_ptr<T>(new T(__VA_ARGS__))

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

Вроде нормальное желание - «подчеркнуть маркером» опасные места, также понятно стремление уменьшить кол-во таких мест к минимуму. Но люди упорно хотят превратить всё в мазню, где найти ошибку/убедиться в валидности будет сложно. По тем же причинам нужно избегать лишнего употребление static_cast, обернув его для какой-то элементарщины вроде целочисленных кастов. По таким мелочам легко определить хелоу ворлд первокурсника от серьёзной поделки.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Не, не аргумент

Аргументы в споре, а я не спорю с вами, лишь показываю многообразие вариантов, о которых вы и не думаете.

достаточно сделать один макрос:

Заменить простой и понятный new на макрос, смысл и способ работы которого понятен лишь автору макроса? ok.jpg

серьёзной поделки

Э… оксюморон?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Формально до C++20 это UB (в C++20 включили P0593)

Да, похоже действительно, с цпп20 данный трюк должен работать без всякого ЮБ (если нет косяков с выравниванием):

unsigned char buf[...];
//int buf[...];  UB
read_from_file(file, buf);
Header *h = reinterpret_cast<Header*>(buf);
kvpfs ★★
()
Ответ на: комментарий от kvpfs

если нет косяков с выравниванием

В смысле? Выравнивание и размер - это и есть основные причины, почему это УБ.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Оптимизации. Можно не читать заново в регистр по указателю в некоторых случаях, если компилятор считает, что два указателя на данные типы не могут друг друга алиасить.

kvpfs ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.