使用std::dynamic_pointer_cast进行up-casting std::shared_ptr

10

我开始学习在C++0X/11中使用智能指针,并遇到了一个奇怪的情况。我想使用shared_ptr向上转型一个对象实例。

类Extend继承自类Base,其中Base类有一个虚析构函数以使其具有多态性(否则dynamic_pointer_cast会抱怨非多态类强制转换)。

因此:

std::shared_ptr<Base> obj = std::make_shared<Base>();

然后我做:

obj = std::dynamic_pointer_cast<Extend>(obj);
  1. 它安全吗?
  2. 其他指向该对象的指针会发生什么?只有obj将其视为Extend,而其他共享指针仍将其视为Base吗?
  3. 对于同一实例进行向上转换是否安全,还是应该采取其他措施?

编辑:感谢答案。我提出这个问题的真正原因是使用SAX解析器处理XML文档,但我被上下转换搞混了。

std::shared_ptr<Extend> ex = std::dynamic_pointer_cast<Extend>(obj);
obj = ex;

但这毫无意义,我将使用一个对象工厂。


4
给定 class B {}; class D : B {},从 B* 到 D* 的转换是向下转换(down casting),而从 D* 到 B* 的转换是向上转换(up casting)。我感觉你把方向搞反了。 - David Rodríguez - dribeas
2个回答

10

这不是向上转型,而是向下转型(你正在将指针从一个派生类较少的类转换为更多派生类)。 然而:

安全吗?

是的,是安全的,但由于您正试图将指向运行时类型不是Extend的对象的指针进行向下转换,因此您将得到一个空指针。

其他指向对象的指针会发生什么?只有obj将其视为Extend,而其他共享指针仍将其视为Base吗?

您在这里存在误解:将指针向下转换为一个对象并不会改变该对象。 如果对象不是目标类型,则不会获得指向其的向下转换指针。 指向任何对象的指针的类型在编译时确定,并且不会更改。

对同一实例进行向上转型是否安全,还是我应该做其他事情?

不确定您在这里的意思,这个问题的表述可能源自上述误解。不过,这个指示:

obj = std::dynamic_pointer_cast<Extend>(obj);

obj 变为空指针。此赋值本身是合法的,因为你可以将派生类的 (智能) 指针赋给基类的 (智能) 指针。但是,由于赋值右侧的值评估为空指针(因为上面所述),最终会将空指针分配给 obj

由于您基本上只是重置了obj,如果 obj 是通过 make_shared<>() 创建的对象的最后一个共享指针,那么在执行上述赋值后,该对象将被销毁。


是的,当然,我刚意识到我走错了路。谢谢。我一直在尝试将一个基类成员从基类向派生类进行上转换。 - Ælex
最后一个问题肯定是关于将obj进行类型转换,然后将结果存回到同一个obj变量中。 - Rob Kennedy

7

只有在底层对象确实是该类型时,才能向下转换为超类型。强制转换不能使底层对象获得新属性。

std::make_shared<Base>();

在底层调用时,Will会调用new Base

这意味着你不能:

obj = std::dynamic_pointer_cast<Extend>(obj);

你不能像使用dynamic_castnew Base转换为它不是的类型一样。你需要使用make_shared<Extend>创建一个对象,然后使用shared_ptr<Base>传递它。


是的,当我尝试从类扩展中调用一个方法时,我就想通了。有趣的是编译器没有抱怨我进行向上转型的那一行代码。所以向上转型一个std::shared_ptr是不可能的? - Ælex
2
不,这并不是不可能的,但只有在底层对象确实是该类型时,你才能将其向下转型为超类型。转型不能突然赋予底层对象新的属性。 - Benj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接