向下投射具有附加功能的派生类的共享指针-这样安全吗
Downcasting shared pointer to derived class with additional functionality - is this safe?
考虑以下大纲:
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类型对象的指针。
第二个问题,这是有道理的,对吧?事实上,我正在通过使用指向基类对象的指针而不是基类对象本身来避免切片?
如果我"知道"某个指针指向派生对象,那么我将使用std::dynamic_pointer_cast
来强制转换智能指针。
第三个问题,这在没有警告的情况下编译并且有效,但它安全吗?有效的它会打破共享指针的引用计数方面,并在我预期之前delete
我的对象或delete
它们失败吗?
最后,我可以使用复制构造函数或通过赋值来执行此强制转换,如p1和p2所示。有没有首选/正确的方法?
类似问题:
- 下行广播shared_ptr<基础>到shared_ptr<派生>?:这非常接近,但是dervied类没有像我的那样添加额外的功能,所以我不确定它是否完全相同。此外,它使用
boost::shared_ptr
,而我使用的是std::shared_ptr
(尽管我知道boost向std库捐赠了shared_ptr,所以它们可能是相同的)
谢谢你的帮助。
编辑:
我问的一个原因是,我意识到以下可能是(错误的):
/* 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类中实现的方法。换句话说,所指向的对象没有定义(或者甚至没有"意识到"该方法)。
编译器不会捕捉到这一点,但可能会导致segfault,具体取决于AdditionalFunctionality
的定义方式。
Base
是否有虚拟析构函数?如果是,那么使用下行广播是安全的。在你不正确的样本中,pDerived
的结果应该是NULL
,所以你每次都需要检查dynamic_pointer_cast
的结果。
如果容器中不应该有基对象(我不能从这个问题中判断出来,但这是您的编辑所暗示的),那么您应该让容器包含派生对象,然后您可以自动访问附加函数。
如果容器可以同时具有这两种类型的对象,那么您似乎希望能够将所有对象视为该容器中的基类。在这种情况下,你几乎肯定想使用多态性来做正确的事情:有一个虚拟接口,基本上说"做这项工作",而父版本可能什么都不做。然后,该方法的子版本实现了所需的附加功能。
我认为您可能有一种代码气味,即您的对象的相关性比您想象的要低。您继承是为了重用,还是为了允许替换?您可能还需要重新考虑您的公共界面是什么样子的。
话虽如此,如果你决定继续你目前的设计(我至少会强烈审查),我认为你的下转换应该是安全的,只要你在使用它之前检查动态转换的结果是否为非null。
好的,首先,如果你这样做,你需要确保Base有一个虚拟析构函数。否则,当矢量超出范围时,将得到未定义的行为。(向量的析构函数将为其每个元素调用Base
的析构因子。如果任何元素真的是Derived
-KABOOM!)除此之外,您所写的内容是完全安全有效的。
但这有什么意义呢?如果你有一个对象容器,你希望能够对它们一视同仁。(循环所有它们,并对每个调用一个函数,或者其他什么。)如果你不想对它们一视同仁,为什么要把它们放在一个容器里?所以你有一个向量,它可以包含指向Base的指针或指向Derived的指针——你怎么知道哪些元素是哪种类型的?您是否计划在每次想要调用AdditionalFunctionality
进行检查以确保元素真的指向Derived
时,只对每个元素调用dynamic_cast
?这既不高效也不惯用,而且它基本上违背了使用继承的全部意义。你还不如使用一个带标签的联合。
你用错工具了。当人们告诉你不要这样做时,并不是因为不安全或无效,而是因为你最终会使代码变得比需要的更复杂。
- 如何从具有移动语义的类对象中生成共享指针
- 使用共享指针的函数调用,其对象应为 const
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 如何访问由共享指针保存的类方法?
- 从矢量或地图中删除共享指针
- 使用共享指针时,从共享指针本身释放内存的机制是什么
- 使用共享指针实现复制 c'tor?
- C++/QT:使用指向私有成员的常量指针作为只读数据共享
- 共享指针继承,而不先显式强制转换
- 如何检查类中共享指针的有效性?
- 共享 C++ 的数据成员指针
- std::排序在共享指针的向量上
- 将相同共享指针的副本存储在不同的向量中是否是一种好的做法?
- 在什么情况下,需要共享智能指针而无法使用唯一指针?
- 嵌套类、继承和C++中的共享指针
- <Base> <Derived> 具有相同原始指针共享引用的 shared_ptr 和 shared_ptr 实例是否计数?
- 重载流<<运算符,用于指针/共享指针和其他类型的
- RAW指针共享_ptr
- 指针共享习语
- 将memmove函数与临时指针共享相同的地址