是否将shared_ptr <Base>向下转换为shared_ptr <Derived>?


102

更新:此示例中的shared_ptr类似于Boost中的shared_ptr,但它不支持shared_polymorphic_downcast(或者对于此问题,不支持dynamic_pointer_cast或static_pointer_cast)!

我正在尝试初始化指向派生类的共享指针,而不会丢失引用计数:

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

到目前为止,一切都很好。我没想到C ++会将Base *隐式转换为Derived *。但是,我确实想要代码表示的功能(即在向下转换基本指针的同时保持引用计数)。我的第一个想法是在Base中提供一个强制转换运算符,以便可以隐式转换为Derived(对于pedants:我会检查向下强制转换是否有效,不用担心):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

好吧,这没有帮助。看来编译器完全忽略了我的类型转换运算符。有什么想法可以使shared_ptr分配工作吗?需要特别说明的Base* const是:哪种类型? const Base*我了解,但是Base* constconst在这种情况下指的是什么?


为什么您需要shared_ptr <Derived>而不是shared_ptr <Base>?
条例草案

3
因为我想访问Derived中不存在于Base中的功能,而不克隆对象(我想要一个由两个共享指针引用的对象)。顺便说一下,为什么演员没有工作?
Lajos Nagy

Answers:


109

您可以使用dynamic_pointer_cast。受到的支持std::shared_ptr

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

说明文件:https//en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

另外,我不建议在基类中使用强制转换运算符。这样的隐式转换可能会成为错误和错误的来源。

-更新:如果类型不是多态的,std::static_pointer_cast则可以使用。


4
从第一行中我不了解他没有使用std::shared_ptr。但是从第一个答案的评论中,我推断他没有使用boost,所以他可能正在使用std::shared_ptr
Massood Khaari 2013年

好。抱歉。他应该更好地说明他正在使用自定义实现。
Massood Khaari 2013年

47

我认为您正在使用boost::shared_ptr...我认为您想要dynamic_pointer_castshared_polymorphic_downcast

但是,这些需要多态类型。

什么类型Base* const的?const Base*我了解,但是Base* constconst在这种情况下指的是什么?

  • const Base *是指向常量的可变指针Base
  • Base const *是指向常量的可变指针Base
  • Base * const是可变的常量指针Base
  • Base const * const是指向常量的常量指针Base

这是一个最小的示例:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

我不确定您的示例是否故意创建基本类型的实例并将其强制转换,但这可以很好地说明这种差异。

static_pointer_cast会“只管去做”。这将导致未定义的行为(Derived*指向由分配并初始化的内存Base),并可能导致崩溃甚至更糟。参考计数base将增加。

dynamic_pointer_cast将导致一个空指针。参考计数base不变。

shared_polymorphic_downcast将具有相同结果作为静态的演员,但会触发一个断言,而不是似乎取得成功,并导致不确定的行为。参考计数base将增加。

参见(死链接)

有时,很难决定是否要使用static_castdynamic_cast,而您希望同时拥有这两个世界。众所周知,dynamic_cast具有运行时开销,但更安全,而static_cast完全没有开销,但它可能会静默失败。如果可以shared_dynamic_cast在调试版本和shared_static_cast发行版本中使用,那就好了。嗯,这样的事情已经存在并称为shared_polymorphic_downcast


不幸的是,您的解决方案依赖于有意排除在我们正在使用的特定shared_ptr实现中的Boost功能(不要问为什么)。至于const的解释,现在更有意义了。
Lajos Nagy

3
除了实现其他shared_ptr构造函数(采用static_cast_tagdynamic_cast_tag)以外,您无能为力。您在外面做的任何事情都shared_ptr将无法管理引用计数。-在“完美”的OO设计中,您始终可以使用基本类型,而无需知道或关心派生类型是什么,因为它的所有功能都是通过基本类接口公开的。也许您只需要重新考虑一下为什么首先需要向下转换。
Tim Sylvester,2009年

1
@Tim Sylvester:但是,C ++不是一种“完美的” OO语言!:-)垂头丧气的事情在一种非完美的OO语言中
应有尽有

4

如果有人通过boost :: shared_ptr来到这里...

这是您如何向下转换到派生的Boost shared_ptr的方法。假设Derived从Base继承。

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

确保“基础”类/结构至少具有一个虚函数。虚拟析构函数也可以工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.