Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Октябрь 17, 2018, 08:28



Название: QScopedPointer
Отправлено: Igors от Октябрь 17, 2018, 08:28
Добрый день

QScopedPointer (или его std аналог) как член класса довольно удобен, пусть и не делает великих дел. Но вот беда - класс сразу становится некопируемым по умолчанию. Объявлять шаред явно нехорошо, ведь ничего шарить не собирался, скорее наоборот. Какие есть др решения?

Спасибо


Название: Re: QScopedPointer
Отправлено: Old от Октябрь 17, 2018, 10:24
QScopedPointer (или его std аналог) как член класса довольно удобен, пусть и не делает великих дел. Но вот беда - класс сразу становится некопируемым по умолчанию. Объявлять шаред явно нехорошо, ведь ничего шарить не собирался, скорее наоборот. Какие есть др решения?
Так вы определитесь, нужно копирование или нет?
Этот указатель владеет ресурсом, на который указывает. Вы копируете объект с таких указателем, кто после этого должен владеть этим ресурсом: исходный объект или его копия?


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 17, 2018, 16:13
Если необходим указатель, который ведет себя как экземпляр объекта, то не припомню такого в std или Qt.
Но его можно легко реализовать как-то так (код не проверял, но вроде должен работать).

Код
C++ (Qt)
template< typename _Type >
class InstancePtr
{
   using ThisType = InstancePtr< _Type >;
 
   using RawPtr = _Type *;
   using RawRef = _Type &;
   using ConstRawPtr = const _Type *;
   using ConstRawRef = const _Type &;
 
   RawPtr m_pointer;
 
public:
   template < typename ... _Arguments >
   InstancePtr ( _Arguments && ... arguments )
       : m_pointer( new _Type( ::std::forward< _Arguments >( arguments ) ... ) )
   {}
 
   InstancePtr ( ThisType && other )
       : m_pointer( ::std::forward< PointerType >( other.m_pointer ) )
   {
       other.m_pointer = nullptr;
   }
 
   InstancePtr ( const ThisType & other )
       : InstancePtr( *other.m_pointer )
   {}
 
   ~InstancePtr () { delete m_pointer; }
 
   RawRef operator * () { return *m_pointer; }
   RawPtr operator -> () { return m_pointer; }
   ConstRawRef operator * () const { return *m_pointer; }
   ConstRawPtr operator -> () const { return m_pointer; }
};
 


Название: Re: QScopedPointer
Отправлено: Авварон от Октябрь 17, 2018, 21:33
Какие есть др решения?

мувать


Название: Re: QScopedPointer
Отправлено: Igors от Октябрь 18, 2018, 08:30
Но его можно легко реализовать как-то так (код не проверял, но вроде должен работать).
Верю :) Но тут эффект что я не раз наблюдал: нужно именно "стандартный" (вумный) указатель, хочет человек или нет, а справочник откроет, жизнь заставит. А "велик" может быть самый распрекрасный, но.. другим-то надо в него вникать, да еще и с нуля и без доки, это вызывает недовольство.

Типовая ситуевина - да, вот есть член которого "может и не быть" (isNull), т.е. scoped по смыслу подходит, но нужно класть экземпляры класса в контейнер. Приходится перекрывать конструктор/оператор копирования, хотелось бы без этого.


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 18, 2018, 08:50
мувать

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

Мувать нужно и в том и в другом случае, собственно, как и копировать. В случае с указателем выше move дешевле, копирование дороже).
Всяких указателей можно наделать сколько угодно много, обеспечить, например, с помощью них потокобезопасность (https://habr.com/post/328348/).
Главное - сформулировать ожидаемое поведение и вперед...)

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

Стандартные указатели декларируют конкретное поведение, которое покрывает далеко не весь спектр возможных задач.
Имхо, именование их shared и unique еще и могут ввести в заблуждение, что это понятия объектно-ориентированной модели).

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


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 18, 2018, 08:57
Типовая ситуевина - да, вот есть член которого "может и не быть" (isNull), т.е. scoped по смыслу подходит, но нужно класть экземпляры класса в контейнер. Приходится перекрывать конструктор/оператор копирования, хотелось бы без этого.

Это ещё один сценарий поведения для OptionalPtr ))).
Вообще, это тема умных указателей достаточно обширная и вполне можно еще раз ее обсудить подробнее, если есть желание).
Последний раз она затрагивалась здесь http://www.prog.org.ru/index.php?topic=32109


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 18, 2018, 10:23
Типовая ситуевина - да, вот есть член которого "может и не быть" (isNull), т.е. scoped по смыслу подходит, но нужно класть экземпляры класса в контейнер. Приходится перекрывать конструктор/оператор копирования, хотелось бы без этого.

Вы так и не ответили на вопрос Old'а: нужно копирование или нет? Опять все догадываться должны, что Вы там себе придумали? Чтобы покласть объекты в контейнер, не обязательно их копировать, можно перемещать.

Допустим таки надо копировать.

1. Если совсем лень, можно попробовать использовать std::optional. Но затея не из лучших.
2. Написать метод, который копирует объект по указателю в том смысле, в каком это нужно для заданного класса. Навскидку:
Код
C++ (Qt)
std::unique_ptr<SomeClass> makeCopy(const std::unique_ptr<SomeClass>& from);
Соответственно придётся написать конструкторы/операторы копирования. И лучше использовать std::unique_ptr, чем QScopedPointer.
3. Написать обертку с нужным поведением, как предлагает ssoft.


Название: Re: QScopedPointer
Отправлено: Old от Октябрь 18, 2018, 10:32
Вы так и не ответили на вопрос Old'а: нужно копирование или нет? Опять все догадываться должны, что Вы там себе придумали?
Это по тому, что о таких мелочах как "владения" никто не думал. Добавил умных указателей, перестало компилироваться - пришел спросить что делать. :)


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 18, 2018, 11:07
Это по тому, что о таких мелочах как "владения" никто не думал. Добавил умных указателей, перестало компилироваться - пришел спросить что делать. :)

Ну хоть спрашивать начинают, и то хорошо. А то обычный подход: "Не получается с unique_ptr? Меняем на shared_ptr и ... в продакшн." :).


Название: Re: QScopedPointer
Отправлено: Racheengel от Октябрь 18, 2018, 11:18
QScopedPointer (или его std аналог) как член класса довольно удобен, пусть и не делает великих дел. Но вот беда - класс сразу становится некопируемым по умолчанию. Объявлять шаред явно нехорошо, ведь ничего шарить не собирался, скорее наоборот. Какие есть др решения?
Так вы определитесь, нужно копирование или нет?
Этот указатель владеет ресурсом, на который указывает. Вы копируете объект с таких указателем, кто после этого должен владеть этим ресурсом: исходный объект или его копия?

ИМХО, было бы приемлемо создание копий таких поинтеров, но с условием, что по умолчанию класс-копия не уничтожает ресурс при своем уничтожении (если при копировании это не указано явно). Ну то есть обычный use case - это передать "умный" указатель куда-то во временную функцию. При этом с ресурсом как правило ничего трагичного происходить не планируется.


Название: Re: QScopedPointer
Отправлено: Igors от Октябрь 18, 2018, 12:10
Всяких указателей можно наделать сколько угодно много, обеспечить, например, с помощью них потокобезопасность (https://habr.com/post/328348/).
Главное - сформулировать ожидаемое поведение и вперед...)
Вот именно, "наделать" можно, но дальше велика это никуда не пойдет. С интересом прочитал статью, оказывается можно как-то хитро привязаться к оператору ->, не знал. Да, познавательно, но.. на этом все и кончается - практически никто и никогда таких огородов городить не будет. Слишком сложно по сравнению с достигаемым рез-том. Ну и вообще
Цитировать
int & a = container[ i ];
Если контейнеры позволяют такую конструкцию, то они по своей природе не могут быть "потокобезопасны", т.к. время жизни ссылки неизвестно и др нитки прорваться не смогут. Можно создать какой-то ограниченный вариант, это интересно, хотя и жить не будет. C'est la vie

1. Если совсем лень,
...
Вы сказали много интересного и нового, но то что интересно - не ново, а то что ново - не интересно" :)

Ну хоть спрашивать начинают, и то хорошо. А то обычный подход: "Не получается с unique_ptr? Меняем на shared_ptr и ... в продакшн." :).
Мне известно что нормального стандартного решения здесь нет, и обвинять QScopedPointer нет оснований. В конце-концов член "просто указатель" (от которого сейчас модно воротить нос) тоже потребует перекрытия копирования/присваивания - но никаких ошибок не выдаст и загнется в runtime, в этом плане QScopedPointer если и не лучше, то корректнее.

ИМХО, было бы приемлемо создание копий таких поинтеров, но с условием, что по умолчанию класс-копия не уничтожает ресурс при своем уничтожении (если при копировании это не указано явно). Ну то есть обычный use case - это передать "умный" указатель куда-то во временную функцию. При этом с ресурсом как правило ничего трагичного происходить не планируется.
Часто заводят флажок с именем типа "shouldDelete" :) А отдавать - да. "голый", лучшего не видно

Что касается "сущностей" и.т.п. Ну вот есть "просто член класса", агрегатом, не указатель  - он почему-то прекрасно копируется. Интуитивно QScopedPointer - такой же член, который однако может быть и null. И что с того - почему он не копируется? Чему это противоречит? Оригинал владеет своим экземпляром, копия - своим, "уникальность" соблюдена. Как-то нынешнее поведение неестественно  :)


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 18, 2018, 12:33
Вы сказали много интересного и нового, но то что интересно - не ново, а то что ново - не интересно" :)

Т.е. Вы сами всё знаете. Отлично :).

Что касается "сущностей" и.т.п. Ну вот есть "просто член класса", агрегатом, не указатель  - он почему-то прекрасно копируется. Интуитивно QScopedPointer - такой же член, который однако может быть и null. И что с того - почему он не копируется? Чему это противоречит? Оригинал владеет своим экземпляром, копия - своим, "уникальность" соблюдена. Как-то нынешнее поведение неестественно  :)

Напишите про это в комитет по стандартизации С++, а то "мужики-то не знают" :).


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 18, 2018, 12:38
ИМХО, было бы приемлемо создание копий таких поинтеров, но с условием, что по умолчанию класс-копия не уничтожает ресурс при своем уничтожении (если при копировании это не указано явно). Ну то есть обычный use case - это передать "умный" указатель куда-то во временную функцию. При этом с ресурсом как правило ничего трагичного происходить не планируется.

А пожалуйста (https://gitlab.com/UnifiedPointers/Library/UPL/blob/master/doc/ru/Reference.md#%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F).


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 18, 2018, 14:41
Всяких указателей можно наделать сколько угодно много, обеспечить, например, с помощью них потокобезопасность (https://habr.com/post/328348/).
Главное - сформулировать ожидаемое поведение и вперед...)
Вот именно, "наделать" можно, но дальше велика это никуда не пойдет.

Почему велик-то? В программе же объявляются типы для хранения данных и формирования нужного поведения.
Это такой же пользовательский тип, как и все другие. Назовите его не указателем (тем более, что желаемая сущность им и не является), а HeapInstance или HeapOptional, например.


Название: Re: QScopedPointer
Отправлено: kamre от Октябрь 18, 2018, 19:15
Ну вот есть "просто член класса", агрегатом, не указатель  - он почему-то прекрасно копируется. Интуитивно QScopedPointer - такой же член, который однако может быть и null. И что с того - почему он не копируется? Чему это противоречит? Оригинал владеет своим экземпляром, копия - своим, "уникальность" соблюдена.
Для "такой же член, который однако может быть и null" достаточно std::optional, зачем какие-то умные указатели нужны?


Название: Re: QScopedPointer
Отправлено: Igors от Октябрь 19, 2018, 08:49
Т.е. Вы сами всё знаете. Отлично :).
...
Напишите про это в комитет по стандартизации С++, а то "мужики-то не знают" :).
Вы не замечали что люди, поначалу весьма активно спрашивающие, затем покидают форум? Думаю потому что как "источник знаний" (или "живой букварь") любой форум малоэффективен. Хотя бы эта тема - ну вряд ли кто-то из присутствующих чего-то "не знал" :) Но интересны не "знания" (которые проще почерпнуть из справочника), а мысли и оценки - какое решение оптимально в данной ситуации и почему? А Вы шпарите "по книжке"  :)

Для "такой же член, который однако может быть и null" достаточно std::optional, зачем какие-то умные указатели нужны?
Разумно, но эта замена не эквивалентна. Напр QScopedPointer может указывать на полиморфный тип. Вообще чисто технически "выйгрышь" от него невелик - подумашь, сэкономил строчку в деструкторе, по сути и все. Правда есть ценная возможность swap - ну это редко. Главное что объявив QScopedPointer я четко заявляю "владение", это вносит ясность которой "просто указатель" не имеет.

Почему велик-то? В программе же объявляются типы для хранения данных и формирования нужного поведения.
Это такой же пользовательский тип, как и все другие. Назовите его не указателем (тем более, что желаемая сущность им и не является), а HeapInstance или HeapOptional, например.
Я назвал CDeepPointer, сделал ро образцу ::any c помощью Вашей и _Bers (спасибо товарищам). К сожалению, проблемы "легализации" или "статуса" довольно болезненны. Поэтому в основном все равно используются решения которые, на мой взгляд, объективно хуже, но стандартны и поэтому всем понятны. Напр в данном случае - перекрыть конструктор копирования, что бывает не так уж тривиально (см замечание про полиморфный тип)


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 19, 2018, 10:58
Главное что объявив QScopedPointer я четко заявляю "владение", это вносит ясность которой "просто указатель" не имеет.

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

А вот для контейнера (композитного агрегатора) "владение" является неотъемлемой частью его сущности.
Так как агрегация - это частный случай ассоциации, то она может быть реализована на основе любого вида указателей (raw, unique/scoped, shared и т.п.).

Я назвал CDeepPointer, сделал ро образцу ::any c помощью Вашей и _Bers (спасибо товарищам).

Ну и правильно  ;)


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 19, 2018, 11:29
Но интересны не "знания" (которые проще почерпнуть из справочника), а мысли и оценки - какое решение оптимально в данной ситуации и почему?

 ;D Отличный заряд бодрости до конца дня. Спасибо! Эта фраза, произнесённая Вашими устами, звучит особенно пикантно. Ведь все Ваши темы начинаются с сообщения, в котором детальнейшим образом описаны поставленная задача, конкретная ситуация и критерии оптимальности. А название темы подобрано так кратко и ёмко, что уже только по нему можно предложить оптимальное решение.

Я назвал CDeepPointer, сделал ро образцу ::any c помощью Вашей и _Bers (спасибо товарищам).

Робкий вопрос из зала: "А это разве не велосипед?". Ведь:
Но тут эффект что я не раз наблюдал: нужно именно "стандартный" (вумный) указатель, хочет человек или нет, а справочник откроет, жизнь заставит. А "велик" может быть самый распрекрасный, но.. другим-то надо в него вникать, да еще и с нуля и без доки, это вызывает недовольство.
В CDeepPointer вникать не надо, он вызывает довольство? :)


Название: Re: QScopedPointer
Отправлено: Igors от Октябрь 19, 2018, 15:01
Ведь все Ваши темы начинаются с сообщения, в котором детальнейшим образом описаны поставленная задача, конкретная ситуация и критерии оптимальности. А название темы подобрано так кратко и ёмко, что уже только по нему можно предложить оптимальное решение.
Дв, "краткость - сестра таланта"  :)

Робкий вопрос из зала: "А это разве не велосипед?".
...
В CDeepPointer вникать не надо, он вызывает довольство? :)
Точно так же как и Ваши творения :) Как же Вы посмели (а ведь посмели) предложить свою собсную трактовку вумных указателей? Это что, выходит все что мы так добросовестно учили - оказывается вяло, блекло, маловыразительно, "сущностей" никак не выражает, да и вообще "бедно"? Та не может быть!!! Там же (в комитете?) такие вумные дядьки, они плюсы знают в 100 раз лучше! А этот крендель/чувак кто? Он че, такой умный? И.т.п. (уже с переходом на личности)

Это совершенно нормальная, естественная реакция на абсолютно ЛЮБУЮ самостоятельную мысль/решение. Человека можно понять, он "учил", тратил свое время и силы (читай - запоминал), признать что это "не совсем то" или хотя бы "всего лишь один шаг к истине" посильно немногим. Ну и объективные основания есть. Требовать знания std - вполне можно, а вот знания/понимания моих (Ваших, еще кого-то) личных наработок - нет. Все-таки программирование во многом технология/конвейер, и считаться с этим приходится


Название: Re: QScopedPointer
Отправлено: ViTech от Октябрь 19, 2018, 15:40
Дв, "краткость - сестра таланта"  :)

Талантище тогда у Вас просто огроменного размера :).

Точно так же как и Ваши творения :) Как же Вы посмели (а ведь посмели) предложить свою собсную трактовку вумных указателей? Это что, выходит все что мы так добросовестно учили - оказывается вяло, блекло, маловыразительно, "сущностей" никак не выражает, да и вообще "бедно"? Та не может быть!!! Там же (в комитете?) такие вумные дядьки, они плюсы знают в 100 раз лучше! А этот крендель/чувак кто? Он че, такой умный? И.т.п. (уже с переходом на личности)

Это совершенно нормальная, естественная реакция на абсолютно ЛЮБУЮ самостоятельную мысль/решение. Человека можно понять, он "учил", тратил свое время и силы (читай - запоминал), признать что это "не совсем то" или хотя бы "всего лишь один шаг к истине" посильно немногим. Ну и объективные основания есть. Требовать знания std - вполне можно, а вот знания/понимания моих (Ваших, еще кого-то) личных наработок - нет. Все-таки программирование во многом технология/конвейер, и считаться с этим приходится

К чему этот плач Ярославны? :) Я говорил о том, что сначала Вы на предложение сделать класс с нужным поведением сетуете, что это велосипед, нужно вникать, вызывает недовольство, потом ровно такой велосипед и делаете. Непоследовательно как-то. Не ново и не интересно. Предложили бы какой-нибудь крышесносящий вариант, чтобы мы тут все ох...фигели :).


Название: Re: QScopedPointer
Отправлено: kamre от Октябрь 24, 2018, 12:07
Цитировать
Для "такой же член, который однако может быть и null" достаточно std::optional, зачем какие-то умные указатели нужны?
Разумно, но эта замена не эквивалентна. Напр QScopedPointer может указывать на полиморфный тип.
А как можно скопировать объект произвольного полиморфного типа, имея только указатель на базовый класс?


Название: Re: QScopedPointer
Отправлено: ssoft от Октябрь 24, 2018, 12:37
А как можно скопировать объект произвольного полиморфного типа, имея только указатель на базовый класс?

Подобно, как это делает std::any со встроенной фабрикой.


Название: Re: QScopedPointer
Отправлено: Igors от Октябрь 24, 2018, 12:39
А как можно скопировать объект произвольного полиморфного типа, имея только указатель на базовый класс?
Название не помню, но это тот же паттерн что и в QVariant, boost::any а может и где еще


Название: Re: QScopedPointer
Отправлено: kamre от Октябрь 25, 2018, 11:41
Название не помню, но это тот же паттерн что и в QVariant, boost::any а может и где еще
Так они же по значению принимают и хранят объект, а не по указателю как smart pointers.

Видимо в этой теме имеется ввиду какой-то аналог std::any, который ограничивает тип объекта, который хранит. Но инициализировать такой аналог указателем уже нельзя будет.