Нас столько лет учили RAII, а потом бьют по рукам.
Любителям std::shared_ptr посвещается. Вроде бы и да, автоматическое удаление итд,хотя
трудно забыть delete если сделал new, просто согласен
есть не очевидные не решаемые просто моменты которые
обсуждались и здесь , и на хабре, и на sof.
Ну вот допустим хочу я сделать всё в классическом RAII.
Для меня главное всё должно быть в зазубренной последовательности.
Базовый класс, члены, текущий конструктор и наоборот при удалении.
Но если вы вдруг поддаётесь общей тенденции об использовании
стандартных умных указателей первое на что натыкаешься — как получить std::shared_ptr из this.
И оп-па первые грабли.Вроде бы это можно решить использованием std::enable_shared_from_this.
Но std::enable_shared_from_this::shared_from_this нельзя использовать в конструкТОРАХ.
А это ломает вообще всё, даже то что раньше делалось простым наследованием или заменой
наследования включением.
Казалось бы ну есть make-функции. Но во первых их два типа:
std::make_shared без делетера,
статические(темплейт наверное) в базовом классе(мейби).
Но мне надо ограничить методы создания объектов.
Либо можно всё — и на стеке и в куче.
Либо только куча.
Короче я не всё написал, но может кто понял о чём я.
Короче вариант с виртуальным конструктором исключает make_shared(удалённые частичные специализации).
А классический RAII возможен(наверное, ещё не пробовал) только с каким нибудь
boost::intrusive_ptr(abandoned), in_proposal::retain_ptr.
В общем хочу чтобы shared_ptr и классический RAII(в обычных конструкторах) жили вместе как нибудь.
А да забыл про переопределение new.
Ладно у кого какие варианты7
Здравствуйте, dipso, Вы писали:
D>стандартных умных указателей первое на что натыкаешься — как получить std::shared_ptr из this. D>И оп-па первые грабли.Вроде бы это можно решить использованием std::enable_shared_from_this. D>Но std::enable_shared_from_this::shared_from_this нельзя использовать в конструкТОРАХ.
И очень хорошо, что нельзя. Ибо в конструкторе не известна ни область памяти, ни способ, которым объект создается, ни то, какми способом он должен удаляться. Если возникает потребность в получении shared_ptr на объект внутри его конструктора, то, ИМХО, это повод для пересмотра дизайна.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, dipso, Вы писали:
D>>стандартных умных указателей первое на что натыкаешься — как получить std::shared_ptr из this. D>>И оп-па первые грабли.Вроде бы это можно решить использованием std::enable_shared_from_this. D>>Но std::enable_shared_from_this::shared_from_this нельзя использовать в конструкТОРАХ.
R>И очень хорошо, что нельзя. Ибо в конструкторе не известна ни область памяти, ни способ, которым объект создается, ни то, какми способом он должен удаляться. Если возникает потребность в получении shared_ptr на объект внутри его конструктора, то, ИМХО, это повод для пересмотра дизайна.
Ну и ... Свои make-функции поверх make-shared? Мне нужна двухфакторная инициализация?
Во первых, непонятно, почему именно shared_ptr выбран для RAII, а не объект на стеке или unique_ptr. Во вторых, как использование RAII приводит к вызову shared_from_this() в конструкторе. Это ортогональные вещи.
Здравствуйте, dipso, Вы писали:
D>Нас столько лет учили RAII, а потом бьют по рукам.
Здравствуйте, dipso, Вы писали:
D>Короче я не всё написал, но может кто понял о чём я. D>Короче вариант с виртуальным конструктором исключает make_shared(удалённые частичные специализации). D>А классический RAII возможен(наверное, ещё не пробовал) только с каким нибудь D>boost::intrusive_ptr(abandoned), in_proposal::retain_ptr.
Эти справляются с расшариванием в конструкторе. Но не в деструкторе. и weak_ версии у них нет.
D>В общем хочу чтобы shared_ptr и классический RAII(в обычных конструкторах) жили вместе как нибудь. D>А да забыл про переопределение new. D>Ладно у кого какие варианты7
Ну-у, есть вариант, что "классический RAII" — это вообще без счётчиков ссылок, когда известно кто кем владеет.
shared_ptr и intrusive_ptr только когда очень надо. shared_from_this — когда очень-очень.
Чем больше всё под контролем, тем меньше эти проблемы shared_ptr мешают.
Ещё есть языки со сборкой мусора, в плане освобождения памяти сборка мусора считается убийцей RAII.
(Но при этом полностью отключить мозг не позволяет даже сборка мусора )
Здравствуйте, dipso, Вы писали:
R>>И очень хорошо, что нельзя. Ибо в конструкторе не известна ни область памяти, ни способ, которым объект создается, ни то, какми способом он должен удаляться. Если возникает потребность в получении shared_ptr на объект внутри его конструктора, то, ИМХО, это повод для пересмотра дизайна.
D>Ну и ... Свои make-функции поверх make-shared? Мне нужна двухфакторная инициализация?
Зачем? После того, как объект сконструирован и передан во владение, можно будет спокойно пользовастья механизмом enable_shared_from_this — хоть внутри объекта, хоть снаружи, без всяких костылей и двухфазных инициализаций.
Здравствуйте, Alexander G, Вы писали:
D>>А классический RAII возможен(наверное, ещё не пробовал) только с каким нибудь D>>boost::intrusive_ptr(abandoned), in_proposal::retain_ptr.
AG>Эти справляются с расшариванием в конструкторе. Но не в деструкторе. и weak_ версии у них нет.
Зачем вообще может понадобиться владеющий указатель в конструторе? Что можно делать с владеющим указателем на объект, время жизни которого еще не началось? Ну, допустим, получили мы его, что дальше?
Здравствуйте, rg45, Вы писали:
R>Зачем вообще может понадобиться владеющий указатель в конструторе? Что можно делать с владеющим указателем на объект, время жизни которого еще не началось? Ну, допустим, получили мы его, что дальше?
сырой указатель довольно часто используют в конструкторе (передают в базовые классы, например). несырой (более умный) указатель могут использовать по той же причине
да, это немного опасно (даже компилятор иногда ворнинг выдает), надо быть внимательным в обоих случаях, но при некоторой осторожности это работает, как и планируется
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, rg45, Вы писали:
R>>Зачем вообще может понадобиться владеющий указатель в конструторе? Что можно делать с владеющим указателем на объект, время жизни которого еще не началось? Ну, допустим, получили мы его, что дальше?
U>сырой указатель довольно часто используют в конструкторе (передают в базовые классы, например). несырой (более умный) указатель могут использовать по той же причине
Сырой указатель на что, на конструируемый объект? И зачем может понадобиться передавать его в базовый класс, когда его можно там без труда вычислить? Разве что, при виртуальном наследовании? Тем более не понятно, зачем может понадобиться передавать в базовый класс владеющий указатель на конструируемый объект.
U>да, это немного опасно (даже компилятор иногда ворнинг выдает), надо быть внимательным в обоих случаях, но при некоторой осторожности это работает, как и планируется
Все это программирование на грани фола. При передаче указателя на конструируемый объект в базовый класс, объект, адресуемый указателем не только еще не начал свое время жизни, он даже еще не начал конструироваться (поскольку объект базового класса конструируется первым). Для того, чтобы прибегать к таким мерам, должны существовать какие-то веские причины. Есть какой-нибудь относительно несложный пример, показывающий необходимость и оправдывающий такие подходы? Ну и на всякий случай, ошибки проектирования мне не хотелось бы рассматривать как веские причины
Здравствуйте, uzhas, Вы писали:
R>>Есть какой-нибудь относительно несложный пример, показывающий необходимость и оправдывающий такие подходы?
U>накидал такой пример: https://ideone.com/8cDrRG
Ну хорошо, для "сырых" указателей хоть какой-то use case есть. (Хотя, в таких случаях я предпочитаю отдавать ссылку, а не указатель, но не суть). Здесь можно было бы начать задавать вопросы типа "почему наследование, а не агрегация" и т.д. Но не будем, будем считать, что есть обоснование. Но изначально ведь речь шла о необходимости получения в конструкторе именно владеющего указателя на конструируемый объект. Вот теперь бы это еще как-то обосновать.
Здравствуйте, rg45, Вы писали:
R>Ну хорошо, для "сырых" указателей хоть какой-то use case есть.
в данном примере можешь заменить сырой указатель на умный, суть не изменится.
либо класс Car надеется, что ILogger кто-то снаружи держит (хрупко, что с ссылками, что с сырыми указателями), либо сам его держит через умный указатель (уже надежнее, но накладнее)
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, rg45, Вы писали:
R>>Есть какой-нибудь относительно несложный пример, показывающий необходимость и оправдывающий такие подходы?
U>накидал такой пример: https://ideone.com/8cDrRG
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, rg45, Вы писали:
R>>Ну хорошо, для "сырых" указателей хоть какой-то use case есть.
U>в данном примере можешь заменить сырой указатель на умный, суть не изменится. U>либо класс Car надеется, что ILogger кто-то снаружи держит
А с Dependency Injection разве так не должно быть всегда?
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, uzhas, Вы писали:
U>>Здравствуйте, rg45, Вы писали:
R>>>Зачем вообще может понадобиться владеющий указатель в конструторе? Что можно делать с владеющим указателем на объект, время жизни которого еще не началось? Ну, допустим, получили мы его, что дальше?
U>>сырой указатель довольно часто используют в конструкторе (передают в базовые классы, например). несырой (более умный) указатель могут использовать по той же причине
R>Сырой указатель на что, на конструируемый объект? И зачем может понадобиться передавать его в базовый класс, когда его можно там без труда вычислить?
Здравствуйте, uzhas, Вы писали:
R>>Ну хорошо, для "сырых" указателей хоть какой-то use case есть.
U>в данном примере можешь заменить сырой указатель на умный, суть не изменится. U>либо класс Car надеется, что ILogger кто-то снаружи держит (хрупко, что с ссылками, что с сырыми указателями), либо сам его держит через умный указатель (уже надежнее, но накладнее)
Во-первых, речь шла не просто об умных указателях, а о владеющих. А во-вторых, я интересовался примером, который бы показывал НЕОБХОДИМОСТЬ получения в конструкторе владеющего указателя на конструируемый объект. Твой пример такой необходимости не показывает, поскольку здесь можно обойтись без всяких умных указателей. Ну а нет необходимости, нет и проблемы, не так ли?
Здравствуйте, σ, Вы писали:
R>>Сырой указатель на что, на конструируемый объект? И зачем может понадобиться передавать его в базовый класс, когда его можно там без труда вычислить?
σ>:| Как?
Согласен, в общем случае нельзя. Но это и не основной вопрос обсуждения, в то же время.
По-моему, слишком спорный пример, чтобы на его основе что либо показывать. Ну допустим есть такой код. В теме идёт речь о RAII, соответственно о владении объектами и ответственными за их уничтожение. В вашем примере Car владеет logger(должен его уничтожать когда сам удаляется) или нет? Если владеет, то желательно ещё указать тип владения (уникальное/совместное/...?). Для наглядности перепишите этот пример с new и delete. Если Car всё же владеет logger, то какое поведение ожидается, когда владение объектом передаётся самому себе? Какое отношение к этому имеет RAII именно в C++? Можно ли делать подобные вещи в других языках, например со сборкой мусора, в Jave какой-нибудь ?
Прошу прощения, не все ответы прочитал, нет столько времени, но...
Самый простой вариант — реализовать класс Tree пытаясь использовать
нынешнию RAII идеологию. Пишу прямо в тегах, сильно не пинайте.
template<typename T>
class Tree
{
public:
using ptr_type = std::shared_ptr<T>;
using weakptr_type = std::weak_ptr<T>;
Tree(ptr_type parent)
{
SetParent(parent);
}
ptr_type Parent()
{
return m_parent.lock();
}
void SetParent(ptr_type parent)
{
m_parent = parent;
}
private:
weakptr_type m_parent;
std::vector<ptr_type> m_children;
};
Нужно наследование от std::enable_shared_from_this.
Конструктор по умолчанию не важен.Хочу наследовать от
Tree. Включение против наследования не поможет.
Интересуют конструкторы.
В любом случае std::bad_weak_ptr.