Каков наилучший способ передать a shared_ptr
производного типа функции, которая принимает shared_ptr
a базового типа?
Я обычно передаю shared_ptr
s по ссылке, чтобы избежать ненужной копии:
int foo(const shared_ptr<bar>& ptr);
но это не сработает, если я попытаюсь сделать что-то вроде
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
Я мог бы использовать
foo(dynamic_pointer_cast<Base, Derived>(bar));
но это кажется неоптимальным по двум причинам:
- A
dynamic_cast
кажется немного чрезмерным для простого приведения типа производного к основному. - Насколько я понимаю,
dynamic_pointer_cast
создает копию (пусть и временную) указателя для перехода к функции.
Есть ли лучшее решение?
Обновление для потомков:
Оказалось, проблема в отсутствующем файле заголовка. Кроме того, то, что я пытался здесь сделать, считается антипаттерном. В общем-то,
Функции, которые не влияют на время жизни объекта (т. Е. Объект остается действующим на время выполнения функции), должны принимать простую ссылку или указатель, например
int foo(bar& b)
.Функции, которые потребляют объект (т.е. являются конечными пользователями данного объекта), должны принимать
unique_ptr
по значению, напримерint foo(unique_ptr<bar> b)
. Вызывающие должны указатьstd::move
значение в функции.Функции, продлевающие время жизни объекта, должны принимать
shared_ptr
по значению, напримерint foo(shared_ptr<bar> b)
. Применяется обычный совет избегать циклических ссылок .
См. Доклад Херба Саттера « Назад к основам» .
источник
shared_ptr
? Почему нет постоянной ссылки бара?dynamic
приведение нужно только для понижения. Кроме того, передача производного указателя должна работать нормально. Он создаст новыйshared_ptr
с тем же refcount (и увеличит его) и указатель на базу, который затем привяжется к ссылке const. Однако, поскольку вы уже берете ссылку, я не понимаю, почему вы вообще хотите ее использоватьshared_ptr
. ВозьмиBase const&
и позвониfoo(*bar)
.foo(bar)
) не работает, по крайней мере, в MSVC 2010.shared_ptr
для передачи функции? Я почти уверен, что этого не избежать.Ответы:
Хотя
Base
иDerived
являются ковариантными, но необработанные указатели на них будут действовать соответственноshared_ptr<Base>
и неshared_ptr<Derived>
являются ковариантными. Это самый правильный и простой способ справиться с этой проблемой.dynamic_pointer_cast
( Изменить:
static_pointer_cast
было бы более подходящим, потому что вы выполняете преобразование из производного в базовый, что безопасно и не требует проверок во время выполнения. См. Комментарии ниже.)Однако, если ваша
foo()
функция не желает принимать участие в продлении времени жизни (или, скорее, участвовать в совместном владении объектом), то лучше всего принятьconst Base&
и разыменоватьshared_ptr
при передаче вfoo()
.void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);
Кстати, поскольку
shared_ptr
типы не могут быть ковариантными, правила неявных преобразований между ковариантными возвращаемыми типами не применяются при возврате типовshared_ptr<T>
.источник
shared_ptr<Derived>
могут быть неявно преобразованы вshared_ptr<Base>
, поэтому код должен работать без махинаций приведения типов.shared_ptr<Ty>
есть конструктор, который принимаетshared_ptr<Other>
и выполняет соответствующее преобразование, еслиTy*
он неявно конвертируется вOther*
. А если нужна гипсовая повязка,static_pointer_cast
то подходящей здесь нетdynamic_pointer_cast
.shared_ptr
, чтобы избежать подсчета ссылок, то на самом деле нет веских причин использовать ashared_ptr
в первую очередь. Лучше использоватьconst Base&
вместо этого.static_pointer_cast
, спасибо.Это также произойдет, если вы забыли указать публичное наследование производного класса, т.е. если вы, как и я, напишите это:
class Derived : Base { };
источник
class
для параметров шаблона;struct
для определения классов. (Это не более 45% шутки.)Похоже, вы слишком стараетесь.
shared_ptr
дешево копировать; это одна из его целей. Передача их по ссылке на самом деле не дает многого. Если вы не хотите делиться, передайте необработанный указатель.Тем не менее, есть два способа сделать это, о которых я могу подумать:
foo(shared_ptr<Base>(bar)); foo(static_pointer_cast<Base>(bar));
источник
shared_ptr
реализацию, поставляемую Microsoft.Также убедитесь, что
#include
файл заголовка, содержащий полное объявление производного класса, находится в исходном файле.У меня была такая проблема. То
std::shared<derived>
бы не снималиstd::shared<base>
. Я заранее объявил оба класса, чтобы я мог хранить указатели на них, но поскольку у меня не было#include
компилятора, компилятор не мог видеть, что один класс является производным от другого.источник