LINUX.ORG.RU

передать владение unique_ptr указателю на потомка

 


0

3

Имеется типичная фабрика:

struct A;
struct B;
struct Base {
	int y;
	static std::unique_ptr<Base> create( size_t n );
    virtual ~Base() { }
};
struct A : public Base {int a; virtual ~A(){};};
struct B : public Base {int b; virtual ~B(){};};
std::unique_ptr<Base> Base::create( size_t n ){
  	if( n == 1 ) return  std::unique_ptr<Base>(new A );
     else  return  std::unique_ptr<Base>(new B );
}

При создании конкретного класса (A) потребовалось обратится к его уникальному полю (a). Решения этого вопроса удалось выполнить путем извлечения сырого указателя:

auto x = static_cast<A*>( Base::create(1).get() );
x->y=1;
x->a=2;

http://ideone.com/wGKclN

Но, что-то не нравиться мне такое решение, в дальнейшем с указателем x будут реализованы сложные алгоритмы. Переходить на shared_ptr тоже вроде смысла нет, т.к. создали один единственный указатель и с ним работаем.

Можно ли воспользоваться move? как то так

std::unique_ptr<A> z = std::move( x );
z->a=2;



Последнее исправление: mathdev (всего исправлений: 2)

А нельзя сделать шаблонный create и форвардить аргументы дочерним классам через variadic template? Тогда не надо будет получать этот указатель.

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

Перепаковать unique_ptr в другой ptr можно через release() и dynamic_cast. Но ты определённо творишь ерунду, в твоём случае нужно пользоваться темплейтным create как советует предыдущий оратор.

anonymous
()

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

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

приношу извинения

ФАБРИЧНЫЙ МЕТОД

mathdev
() автор топика

unique_ptr - это просто способ вызвать очистку при выходе из блока видимости. Возвращай голый указатель из фабрики и передавай его в конструктор unique_ptr нужного тебе типа. Совать это во все API смысла нет никакого. И слепо следовать этим дебильным в общем случае best practices не нужно тоже.

dzidzitop ★★
()

Переходить на shared_ptr тоже вроде смысла нет, т.к. создали один единственный указатель и с ним работаем.

Разница между unique и shared указателями исключительно в контроле над владением ресурсом. То есть, unique_ptr - способ четко показать, что ресурсом может владеть кто-то один в данный момент времени. Если не стоит таких ограничений - отдавай shared_ptr.

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

так в том то и дело, что в один момент времени будет владеть ресурсом один только указатель --- причем это точно известно по программируемым алгоритмам. А проблема в том, что фабричный метод возвращает unique_prt<Base> и возникла ситуация, в которой требуется поработать с unique_prt<A> (A - наследник Base).

И у меня не получается сделать move от unique_ptr<Base> для получениия unique_prt<A>. Решение через сырой указатель нахожу не плюсовым.

mathdev
() автор топика
Ответ на: комментарий от mathdev

так в том то и дело, что в один момент времени будет владеть ресурсом один только указатель --- причем это точно известно по программируемым алгоритмам

Это не важно. Важно другое - нужно ли тебе накладывать тут такое ограничение?

и возникла ситуация, в которой требуется поработать с unique_prt<A> (A - наследник Base).

У тебя проблема в дизайне. Переписывай. Или добавляй в интерфейс. Апкаст - обычно признак костылей.

invy ★★★★★
()

Плюсую проблему в дизайне. Ты добровольно теряешь тип - делаешь даункаст, а потом хочешь восстановить его обратно. Значит, Base тебе тут не подходит.

anonymous
()
Ответ на: комментарий от dzidzitop

Совать это во все API смысла нет никакого. И слепо следовать этим дебильным в общем случае best practices не нужно тоже.

Да-да, мало еще по граблям с голыми указателями и неявными политиками владения потоптались за последние 30 лет. Обязательно нужно продолжать это делать.

Напомните, уважаемый, вы уже начали писать C++ный код для продакшена?

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

Думайте мантрами типа «goto is considered harmful» без осмысления и дальше.

А весь мой код или в продакшене или в публичном доступе. Количество new/malloc в нём исчезающе мало. Соответственно, и guards типа auto_ptr и прочего - тоже.

И даже невиртуальный деструктор в классе с виртуальными функциями есть.

И почему-то память не течёт. Наверное, проектировать код нужно, чтобы проблемы «владения» не возникало, а не обмазываться обёртками над динамической памятью. А автор топика уже поимел себе проблем на уровне API на базе самых лучших в мире практик от Гуру C++.

И при этом, безусловно, auto_ptr/unique_ptr чрезвычайно полезны. Только, как уже, должно быть, заметил вдумчивый читатель данной темы, не везде.

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

Решение через сырой указатель нахожу не плюсовым.

Шаблоны и наследование вместе неработоспособны.

Единственный вариант - использовать только возможности Base, без приведения указателя вниз по иерархии.

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

А автор топика уже поимел себе проблем на уровне API на базе самых лучших в мире практик от Гуру C++.

Он сам не знает чего хочет из-за чего наделал гогнокод, никто так делать не учит.

anonymous
()
Ответ на: комментарий от dzidzitop

Думайте мантрами типа «goto is considered harmful» без осмысления и дальше.

Давайте вы мне не будете говорить что делать, а я не буду говорить куда вам пойти.

И почему-то память не течёт.

Может потому, что пользуетесь им только вы сами?

Наверное, проектировать код нужно, чтобы проблемы «владения» не возникало

Как раз unique_ptr и предназначен для того, чтобы проблем владения не возникало и это было зафиксировано в коде, а не в комментариях, соглашениях о вызовах или в обещаниях из категории «зуб даю».

А автор топика уже поимел себе проблем на уровне API

Автор топика поимел себе проблем не из-за unique_ptr, о чем вдумчивый читатель мог бы догадаться сразу. Посему совет не использовать unique_ptr и не обращать внимания на best practicies в данном случае выглядит особенно «адекватно».

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

Сходите уже сами туда, куда вы мне не скажете пойти.

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

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

Ты так возбудился увидев unique_ptr, что не заметил что проблема не в нем, и с сырыми указателями будет то же самое.

anonymous
()
Ответ на: комментарий от dzidzitop

Эка у вас подгорело. Во времена C++17 возвращать владеющий голый указатель — это, действительно, неплюсово. Нравится вам или нет.

А проблема ТС-а в том, что у него фабрика по запросу пользователя возвращает объекты разных типов, но почему-то вместо методов:

unique_ptr<A> createA() {...}
unique_ptr<B> createB() {...}
он использует один метод create с возвратом указателя на базу. Использование или неиспользование unique_ptr здесь ничего не изменит. Тут нужно либо createA/createB, либо же переделка интерфейса Base так, чтобы пользователю при использовании Base не приходилось делать касты к A или к B.

Но посоветовать нужно именно использовать голые указатели и игнорирование best practices. Молодца, чё.

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

Плюсую проблему в дизайне.

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

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

Автор хочет dynamic_cast - unique_ptr это не позволяет, в отличие от T *.

Можно ему посоветовать разве что сделать свой умный указатель с template<typename T> operator my_ptr<T>()(); в котором и делать dynamic_cast. Средствами стандартной библиотеки без указателей ничего путного у него не получится.

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

Точнее, шаблонные move версии конструкторов и operator= с dynamic_cast внутри. Приведение типов не поможет с семантикой единоличного владения.

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

Люто минусую shared_ptr. Это плохой совет, из разряда «сделай на пофиг, указатель-то все равно умный

Не вижу ничего плохого в отдаче shared_ptr в общем случае. Больше это зависит от ресурса, часто shared_ptr даже нужен, чтобы гарантировать что пока ты работешь с ресурсом его не убьют.

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

Точнее, шаблонные move версии конструкторов и operator= с dynamic_cast внутри. Приведение типов не поможет с семантикой единоличного владения.

Что ты несёшь, укурок. Для неправильного решения проблемы ему всего лишь нужно сделать аналог std::move + dynamic_cast одной функцией, как то так:

template<typename One, typename Two>
std::unique_ptr<One> unique_cast(std::unique_ptr<Two> &ptr)
{
    if (auto *val = dynamic_cast<One*>(ptr.get())) {
        ptr.release();

        return std::unique_ptr<One>(val);
    }

    return { };
}

anonymous
()
Ответ на: комментарий от dzidzitop

Автор хочет dynamic_cast - unique_ptr это не позволяет, в отличие от T *.

Но вместо dynamic_cast ничто не мешает написать свой Ololo_cast<>().

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

За dynamic_cast канделябром в приличном общесте бьют.

Тема интересная. :)

Можно развернутый ответ на тему, почему dynamic_cast плох?

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

существует мнение, что rtti крайне медлителен. И по сравнению со статическим кастами это действительно так. Но обычно динами каст не дергают в критических местах системы и он не является узким местом. А если дергают - то канделябром, да, по мордасам - правильно.

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

Существует мнение, а существует эксперимент или листинг машинного кода, который создаёт компилятор.

Сударь, наверное, подскажет, чем заменить dynamic_cast, чтобы скастовать T * в SubT * или проверить имплементит ли инстанс какой-то интерфейс и не получить при этом канделябром.

На входе в каждый зоопарк надо написать крупными буквами: best practices are considered harmful.

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

Потому что это признак плохого дизайна. Каждый dynamic_cast порождает лапшу в вызывающем коде, которая может и должна быть разложена по полочкам в вызываемом коде. На любом языке код стараются писать в таком стиле, чтобы любая отдельно взятая точка вызова порождала ветвление вверх по вызовам с минимальным ветвлением после этого вызова. Это кстати особенно хорошо прослеживается в функциональных ЯП, например именно поэтому идеальный лиспокод равномерно разрастается по диагонали. Но и в ООП с этим замечательно справляются с помощью наследования и полиморфизма. А dynamic_cast что делает? Ломает всю иерархию и декомпозицию.

Если ты сталкиваешься с необходимостью уточнения типа, это почти всегда звоночек, что используемые тобой абстракции трещат. Т.е. ты спроектировал иерархию типов, которая как-то работала, но однажды ты напарываешься на что-то непредвиденное в первоначальном плане, и ты делаешь dynamic_cast. Ходят даже слухи, что изначально dynamic_cast собирались назвать ёба_чо_это, но плюсы тогда не умели в кириллицу :(

cc dzidzitop.

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

Сударь, наверное, подскажет, чем заменить dynamic_cast, чтобы скастовать T * в SubT * или проверить имплементит ли инстанс какой-то интерфейс и не получить при этом канделябром.

Подсказал в предыдущем комментарии, но не грех и повторить. Замени его на ёба_чо_это!

staseg ★★★★★
()

тс-у читать букварь про наследование, принцип лисков и всё вот это

/thread

anonymous
()
Ответ на: комментарий от staseg

Потому что это признак плохого дизайна.

Забавно, когда пионэры берутся рассуждать о том, о чём понятия не имеют :-) Это дело такое :-)

А dynamic_cast что делает? Ломает всю иерархию и декомпозицию.

Нет, уважаемый, dynamic_cast ничего не ломает :-) Этот оператор всего лишь на всего отвечает на вопрос: является ли переданный в качестве аргумента в круглых скобках указатель (ссылка), указателем (ссылкой) на переданный в угловых скобках тип? :-) Всё :-) И бывает, что это действительно необходимо (например, во всяких декорациях при подмене объектов-представлений) :-)

Хоть бы труды изобретателя цепепе почитали, а не всякой сомнительной информации с бложиков разной паршивости, прежде чем рассуждать и вводить невежд в заблуждение :-) Лол :-)

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

Забавно, когда пионэры берутся рассуждать о том, о чём понятия не имеют

Забавно - это когда свое невежество выставляют напоказ. О проектировании ничего не слышал, читатель блогов? Или целью было зафлудить ЛОР смайликами?

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

Забавно - это когда свое невежество выставляют напоказ.

Эт да, дело такое :-)

О проектировании ничего не слышал, читатель блогов?

Никакого отношения к проектированию (аля рисование квадратиков и стрелочек для выражения иерархии классов и их взаимосвязей) dynamic_cast не имеет :-) dynamic_cast имеет отношение к языку и делает ровно то, о чём сказано выше :-)

Или целью было зафлудить ЛОР смайликами?

Целью было объяснить что же делает dynamic_cast :-) А то эксперты наговорили много, от «апкастов» до «ломания иерархий», но смысла dynamic_cast так и не раскрыли :-)

anonymous
()
Ответ на: комментарий от staseg

так как проверить имплементит ли инстанс какой-то интерфейс без dynamic_cast? примеры придумать легко - от loggable до пустого маркера thread_safe, которые могут помогать писать плагины и т.д.

да в тот же cout с его статической типизацией может понадобиться что-нибудь записать.

но нет - не поддерживает какой-нибудь unique_ptr ковариантность - значит нинужно.

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

ну и если что, то полиморфизм как и нормальные и «умные» указатели у меня очень редкие гости на плюсах. так что если примеры не нравятся, то придумайте сами.

ну или goto и беззнаковые типы запретите до кучи.

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

точнее, if нужен, чтоб указатель не потёк. но вообще возвращать null pointer - лучше уж эксэпшн кинуть, чтоб неповадно было.

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

а вообще хороший пример как эти разумные указатели делают простые вещи сложными, будучи применёнными в несвойственном им контексте, ибо bad cast тут вместо одной проверки приводит к танцам вокруг unique_ptr и возможному удалению кастуемого объекта.

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

точнее, if нужен, чтоб указатель не потёк.

Ну надо же, таки дошло. Лучше, конечно, поздно чем никогда.

...но вообще возвращать null pointer - лучше уж эксэпшн кинуть, чтоб неповадно было.

Посиди и ещё подумай, может снова дойдёт что уету несёшь. Пока будешь думать за одно посмотри что такое pattern matching.

bad cast тут вместо одной проверки приводит к танцам вокруг unique_ptr

Тут не может быть bad_cast, неуч.

и возможному удалению кастуемого объекта.

До дебя вроде дошло зачем нужен if или всё таки не дошло и знаток возьмёт дополнительную неделю на обсуждение?

anonymous
()
Ответ на: комментарий от dzidzitop

так как проверить имплементит ли инстанс какой-то интерфейс без dynamic_cast?

Не собираюсь доказывать (не)нужность dynamic_cast, но в таких случаях и правда бывает полезно подумать не о том как, а о «зачем». Ведь действительно нередко необходимости проверять можно избежать.

до пустого маркера thread_safe

«Mаркеры» нередко полезны на этапе компиляции и каст там не нужен.

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

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

а отстаивать необходимость его отсутствия (кружок канделябристов) - глупость несусветная.

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

Вроде тут никто не отстаивал необходимость отсутствия? Лишь то что _обычно_ он не нужен и указывает на проблему уровнем выше. Как в ОП.

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