void setQuackBehavior(QuackBehavior & qb) {
quackBehavior = std::move(std::shared_ptr<QuackBehavior> (&qb));
}
Есть метод setQuackBehavior(), который принимает ссылку на абстрактный класс QuackBehavior. Этот метод находится в классе, который имеет член std::shared_ptr<QuackBehavior> quackBehavior.
Может ли код, который приведён выше, иметь право на существование? По-моему тут всё хорошо, но мало ли я что-то не учитываю, и в примере этого кода имеется какая-либо грубая ошибка.
Такой подход имеет ряд недостатков.
1) Когда Вы создаете shared_ptr, выделяется блок динамической памяти для хранения служебных данных, например, счетчик ссылок, собственный delete'ор и т.д.
Таким образом, у Вас будет одно выделение памяти для создания объекта, и одно для создания этого блока служебных данных. И объект и данные также будут лежать в разных участках памяти, поэтому, такая структура, помимо лишнего выделения памяти может еще быть не дружелюбной к кешу.
Для того, чтобы выделить память под объект и указателя рядом, одним выделением, можно использовать std::make_shared. Она за одно выделение памяти сделает и указатель и динамический блок с данными.
2) Для данного shared_ptr не указан пользовательский метод удаления, а значит будет использоваться delete, поэтому объект должен быть создан с помощью new.
3) Объект будет автоматически уничтожен при уничтожении последнего умного указателя ссылки на него. Согласитесь, при вызове setQuackBehavior совершенно не очевидно, что внутри на объект создается shared_ptr.
Derived derived = new Derived;
obj.setQuackBehavior(*derived);
Возможно, когда-нибудь дальше появится что-то вроде delete derived; или еще-что-нибудь подобное.
4) Это скорее не к конкретно этому коду, а вообще к любому коду с shared_ptr. Если два объекта содержат указатели на друг друга (или более сложная схема), то это приведет к циклическим ссылкам и, фактически, к утечкам.
5) Этот код требует гарантии, что на этот объект более нет умных указателей.
std::shared_ptr<Derived> derived(new Derived);//или make_shared
obj.setQuackBehavior(*derived);//Злостная ошибка
При таком (или подобном) коде на один объект derived теперь приходится два блока со служебными данными для разных shared_ptr, а значит у нас и два счетчика ссылок и т.д. Эти указатели теперь живут "раздельно" и не знают о существовании друг друга. Понятное дело, что это приведет к преждевременному удалению объекта, обращению к несуществующим данным, прострелам памяти, двойному удалению и всем-всем прелестям не верной работы с памятью.
Ну и в Вашем коде лишний std::move. Объект и так временный, поэтому будет перемещен, а не скопирован.
Почему бы не сделать так:
void setQuackBehavior(std::shared_ptr<QuackBehavior> const &qb) {
quackBehavior = qb;
}
Сразу очевидно, что работаем с shared_ptr, код Выше его создает, поэтому он может воспользоваться make_shared, что положительно скажется на программе, также очевидно, что раз внутри shared_ptr, то не нужно удалять объект, а также нужно учесть возможность циклических ссылок.
obj.setQuackBehavior(std::make_shared<Derived>());
По-моему, такой код более очевиден и безопасен.
Не очень ясен смысл делать move для shared_ptr. Т.к. при обычном копировании вполне нормально инкрементируется счётчик ссылок.
А ошибка может быть в двойном удалении объекта, на который ссылается qb. Первый раз как обычный объект (надо смотреть код, где создаётся объект исходно), второй - при вызове деструктора последнего объекта std::shared_ptr.
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости