将shared_ptr <Derived>作为shared_ptr <Base>传递

Mat*_*ine 74 c++ casting smart-pointers shared-ptr c++11

shared_ptr派生类型传递给采用shared_ptr基类型的函数的最佳方法是什么?

我通常通过shared_ptr引用传递s以避免不必要的副本:

int foo(const shared_ptr<bar>& ptr);
Run Code Online (Sandbox Code Playgroud)

但如果我尝试做类似的事情,这不起作用

int foo(const shared_ptr<Base>& ptr);

...

shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
Run Code Online (Sandbox Code Playgroud)

我可以用

foo(dynamic_pointer_cast<Base, Derived>(bar));
Run Code Online (Sandbox Code Playgroud)

但这似乎是次优的,原因有两个:

  • dynamic_cast对于简单的派生到基础演员来说,A 似乎有点过分.
  • 据我了解,dynamic_pointer_cast创建一个指向传递给函数的副本(虽然是临时的).

有更好的解决方案吗?

后人更新:

原来这是一个缺少头文件的问题.此外,我在这里尝试做的是一个反模式.通常,

  • 不影响对象生命周期的函数(即对象在函数持续时间内保持有效)应采用普通引用或指针,例如int foo(bar& b).

  • 使用对象的函数(即,是给定对象的最终用户)应该采用unique_ptrby值,例如int foo(unique_ptr<bar> b).std::move调用者应该将值放入函数中.

  • 延长对象生命周期的函数应采用shared_ptrby值,例如int foo(shared_ptr<bar> b).避免循环引用的通常建议适用.

有关详细信息,请参阅Herb Sutter的回归基础讲座.

Bre*_*hns 37

虽然BaseDerived是协变的和原始指针他们会采取相应的行动,shared_ptr<Base>并且shared_ptr<Derived>不是协变的.这dynamic_pointer_cast是处理此问题的正确和最简单的方法.

(编辑: static_pointer_cast更合适,因为你从派生到基础,这是安全的,不需要运行时检查.请参阅下面的评论.)

但是,如果您的foo()函数不希望参与延长生命周期(或者更确切地说,参与对象的共享所有权),那么最好在接受时将其接受const Base&并取消引用.shared_ptrfoo()

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);
Run Code Online (Sandbox Code Playgroud)

顺便说一句,因为shared_ptr类型不能协变,所以返回协变返回类型的隐式转换规则在返回类型时不适用shared_ptr<T>.

  • 它们不是协变的,但是`shared_ptr <Derived>`可以隐式转换为`shared_ptr <Base>`,因此代码应该不使用转换恶作剧. (32认同)
  • 嗯,`shared_ptr <Ty>`有一个构造函数,它接受`shared_ptr <Other>`并且如果`Ty*`可以隐式转换为`Other*`则进行适当的转换.如果需要强制转换,那么`static_pointer_cast`是合适的,而不是`dynamic_pointer_cast`. (7认同)

Pet*_*ker 10

听起来你太努力了.shared_ptr复制便宜; 这是它的目标之一.通过引用传递它们并没有真正实现.如果您不想共享,请传递原始指针.

也就是说,有两种方法可以做到这一点,我可以想到我的头脑:

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));
Run Code Online (Sandbox Code Playgroud)

  • @SethCarnegie - 这不回答我问的问题.而且,为了它的价值,我写了微软发布的`shared_ptr`实现. (22认同)
  • 如果你需要在它上面工作,那只是"过早"的优化.我认为采用有效成语而不是低效成语是没有问题的,无论它是否在特定情境中产生影响. (19认同)
  • 不,他们复制起来并不便宜,应尽可能通过参考传递. (8认同)
  • @SethCarnegie - Herb是否对您的代码进行了分析,以确定通过价值是否是瓶颈? (6认同)
  • @SethCarnegie - 你的启发式向后倾斜.手部优化一般不应该****,除非**你可以证明他们是需要的. (6认同)
  • @ slavik262 - 不,它**有时**应该避免.像任何手动优化一样,如果你有证据证明它是你程序中的瓶颈,就应该这样做.如果我在二十几个地方传递了六个`shared_ptr`对象,那么根本没有理由通过引用传递它们来使事情复杂化. (4认同)
  • @MarkRansom在这种情况下,你*需要在它上面工作,你必须证明`foo`不会调用任何会隐式导致`ptr`被删除的东西,从而导致AV,如果其余的` foo`尝试使用智能指针作为引用.(这实际上是我保留的一个代码库中的一个问题,尽管公平地说,事情可能并不像他们应该的那样"紧张". (4认同)
  • @PeteBecker"除非你能证明他们是需要的,否则通常不应该进行手部优化." 编写`const Foo&`而不是`std :: shared_ptr <Foo>`肯定是我双手的优化.说真的,如果写入时间更短,更清晰,更快,而不是优化,它是强制性的. (3认同)
  • 我只是说Herb Sutter说的话.观看http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything并转到4:34 (2认同)
  • @PeteBecker(关于你编写`shared_ptr`的MSVC实现)然后我发现你不知道Herb Sutter知道你知道什么,这真是令人惊讶.我确实回答了你的问题,而不是你想要的答案.当它像通过引用传递一样微不足道时,它并不会使事情复杂化.没有通过参考传递过早的悲观情绪.为什么按价值传递东西呢?说真的,如果像萨特这样的人推荐一些东西,你就不会改变主意,除非你尊重的人更多地推荐相反的东西,而这并不是在这里发生的. (2认同)

dsh*_*erd 9

如果您忘记在派生类上指定公共继承,也会发生这种情况,即,如果像我一样,您可以这样编写:

class Derived : Base
{
};
Run Code Online (Sandbox Code Playgroud)

  • 这绝对应该被视为解决方案,不需要演员表,因为它只是缺少公众。 (2认同)

Phi*_*erg 7

还要检查#include源文件中是否包含包含派生类的完整声明的头文件。

我有这个问题。该std::shared<derived>不会投给std::shared<base>。我已经预先声明了两个类,以便可以保存指向它们的指针,但是由于我没有#include编译器,因此无法看到一个类是从另一个类派生的。

  • 愚蠢的编译器就是愚蠢的。这是我的问题。谢谢你! (3认同)
  • 哇,我没想到,但这为我修好了。我特别小心,只在我需要的地方包含头文件,所以其中一些只在源文件中,并像你说的那样在头文件中转发声明。 (2认同)