考虑以下概要:
class Base { /* ... */ };
class Derived : public Base
{
public:
void AdditionalFunctionality(int i){ /* ... */ }
};
typedef std::shared_ptr<Base> pBase;
typedef std::shared_ptr<Derived> pDerived;
int main(void)
{
std::vector<pBase> v;
v.push_back(pBase(new Derived()));
pDerived p1( std::dynamic_pointer_cast<Derived>(v[0]) ); /* Copy */
pDerived p2 = std::dynamic_pointer_cast<Derived>(v[0]); /* Assignment */
p1->AdditionalFunctionality(1);
p2->AdditionalFunctionality(2);
/* A */
return 0;
}
这里我使用派生类扩展基类,增加了功能(AdditionalFunctionality
方法)。
第一个问题,这样可以吗?我已经阅读了很多问题,说这不合适,你应该在基类中声明附加功能(通常建议在基类中使它们成为纯虚方法)。但是,我不想这样做。我想扩展基类的功能,而不仅仅是以不同的方式实现它。是否有更好的解决方案来实现这一目标?
好的,所以在这段代码中我也使用一个STL容器来存储这些指针,这些指针允许我存储指向Base类型对象的指针以及Derived类型的对象,而不会切割对象。
第二个问题,这是有道理的,对吧?事实上,我通过使用指向基类对象的指针而不是基类对象本身来避免切片?
如果我“知道”某个指针指向Derived对象,那么我使用std::dynamic_pointer_cast
来转换智能指针。
第三个问题,这个编译没有任何警告和工作,但它是否安全?有效?它会破坏共享指针的引用计数方面,并且在我预期之前无法delete
我的对象或delete
它们吗?
最后,我可以使用复制构造函数或通过赋值执行此转换,如p1和p2所示。有没有一种首选/正确的方法呢?
类似的问题:
boost::shared_ptr
我正在使用std::shared_ptr
(虽然我理解提升捐赠了shared_ptr到std库,所以它们可能是相同的。)感谢您的帮助。
修改
我问的一个原因是我意识到可以完成以下任务(错误):
/* Intentional Error */
v.push_back(pBase(new Base()));
pDerived p3( std::dynamic_pointer_cast<Derived>(v[1]) );
p3->AdditionalFunctionality(3); /* Note 1 */
我尝试将指向Base对象的指针向下转换为Derived对象的指针,然后调用仅在Derived类中实现的方法。换句话说,指向的对象没有定义(或者甚至没有“意识到”该方法)。
编译器没有捕获到它,但可能会导致段错误,具体取决于AdditionalFunctionality
的定义方式。
答案 0 :(得分:9)
Base
是否有虚拟析构函数?如果是,那么使用向下转换是安全的。如果您的错误样本中pDerived
的结果应为NULL
,那么您每次都需要检查dynamic_pointer_cast
的结果。
答案 1 :(得分:3)
如果容器中永远不应该有基础对象(我无法通过编辑暗示问题),那么你应该让容器保持派生对象,然后你可以自动访问附加功能
如果容器可以包含两种类型的对象,那么您似乎希望能够将所有对象视为该容器中的基类。在这种情况下,您几乎肯定希望使用多态来做正确的事情:拥有一个基本上说“做这个工作”的虚拟接口,而父版本可能什么都不做。然后,该方法的子版本实现了您需要的其他功能。
我认为你的代码气味可能与你想象的相关性较低。您是继承重用还是允许替换?您可能还想重新考虑您的公共界面的样子。
所有这一切,如果你决定继续你当前的设计(我至少会强烈评论)我认为你的向下转换应该是安全的,只要你在使用它之前检查非null的动态转换结果
答案 2 :(得分:2)
好的,首先,如果你这样做,你需要确保Base有一个虚拟析构函数。否则,当向量超出范围时,您将获得未定义的行为。 (向量的析构函数将为每个元素调用Base
的析构函数。如果任何元素实际上是Derived
- KABOOM!)除此之外,你所写的内容是完全安全有效的
但重点是什么?如果你有一个容器的容器,你希望能够对它们进行相同的处理。 (循环遍历所有这些并在每个上调用一个函数。)如果你不想对它们进行相同处理,为什么要将它们放在一个容器中?所以你有一个可以保存指向Base的指针或指向Derived的指针 - 你怎么知道哪个元素属于哪种类型?您是否打算每次要调用dynamic_cast
时调用AdditionalFunctionality
每个元素,以检查确保元素确实指向Derived
?这既不高效也不惯用,它基本上都没有使用继承的全部意义。您可能只是使用标记的联合。
你正在使用错误的工具来完成工作。当人们告诉你不要这样做时,并不是因为它不安全或无效,这是因为你最终会使你的代码变得比它需要的更复杂是。