将共享指针的派生类切换到基类

Posted

技术标签:

【中文标题】将共享指针的派生类切换到基类【英文标题】:Switch Derived class of a shared pointer to base class 【发布时间】:2016-10-11 11:21:32 【问题描述】:

我目前正在尝试将存储在共享指针中的派生类的类型切换到基类。 问题是 Derived 类继承自 Base 类,并且模板化如下:

基类:

#define PRINT(s) std::cout << s << std::endl

class Base 
public:
    Base() : m_a(1) 
    virtual ~Base() = default;
    virtual void print()  PRINT("BASE"); 

    int m_a;
;

派生类依赖于枚举模板:

enum eType  e0, e1 ;

template<eType et>
class Derived : public Base  ;

template<>
class Derived<e0> : public Base 
public:
    Derived()  this->m_a = e0; 
    void print()  PRINT("Derived e0, m_a value: " << e0 ); 
;

template<>
class Derived<e1> : public Base 
public:
    Derived()  this->m_a = e1; 
    void print()  PRINT("Derived e1, m_a value: " << e1 ); 
;

我的目标是拥有一个指向基类的共享指针,这样就可以从 2 个派生类切换如下:

int main()

    std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>> ();

    std::shared_ptr<Base> sp_01 = sp_00;
    sp_01->print();

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>> ();

    *sp_01 = *sp_10;

    sp_01->print();
    sp_10->print();

*sp_01 = *sp_10; 行上的唯一问题我希望指向基类的指针从派生类型 Derived&lt;e0&gt; 切换到派生类型 Derived&lt;e1&gt;。但是在我的示例中,sp_01-&gt;print(); 行和sp_10-&gt;print(); 行得到了不同的结果,表明sp_01 保持为Derived&lt;e0&gt; 类型。

我想避免sp_01 = sp_10;,因为它会改变指针。在上面的示例中,它将导致sp_00 != sp_01,我希望sp_00sp_01 共享同一个对象。

我尝试用非模板派生类替换模板派生类,如下所示:

class Derived_e0 : public Base 
public:
    Derived()  this->m_a = e0; 
    void print()  PRINT("Derived e0, m_a value: " << e0 ); 
;
class Derived_e1 : public Base 
public:
    Derived()  this->m_a = e1; 
    void print()  PRINT("Derived e1, m_a value: " << e1 ); 
;

下面的代码给出的结果与带有模板的代码相同。

int main()

    std::shared_ptr<Base> sp_00 = std::make_shared<Derived_e0> ();

    std::shared_ptr<Base> sp_01 = sp_00;
    sp_01->print();

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived_e1> ();

    *sp_01 = *sp_10;

    sp_01->print();
    sp_10->print();

所以我的问题是,如何在不改变shared_ptr本身的情况下切换共享指针指向的派生对象(在程序的其他部分使用?)

谢谢,如果您需要更多信息,请告诉我

【问题讨论】:

如果不重新分配sp_01,就无法更改sp_01 的“多态类型”。如果Derived&lt;e0&gt;Derived&lt;e1&gt; 的大小不同,你怎么能做到这一点?你的设计(或你试图做的)可能有缺陷。 【参考方案1】:

您无法更改 sp_01 的运行时类型而不重新分配它,因为您无法将 Derived&lt;e1&gt; 分配给 Derived&lt;e0&gt;(想想如果它们的大小不同会发生什么 - 您已为Derived&lt;e0&gt;,而不是Derived&lt;e1&gt;!)。

在我看来,你的设计(或你试图用它做什么)在某个地方存在缺陷。但是,如果您真的想在sp_00sp_01 之间保持“链接”,您可能需要另一个“级别”的指针:

int main() 
    std::shared_ptr<Base> *psp_01;

    std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>> ();
    psp_01 = &sp_00;

    (*psp_01)->print();

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>> ();
    psp_01 = &sp_10;

    (*psp_01)->print();
    sp_10->print();

但同样,我会在使用它之前对我的设计进行两次分析。

【讨论】:

感谢您的回复,它适用于预期的行为。我想你对设计是对的,我会尝试改变它。如果我有sp_00sp_01 和其他 shared_ptr 共享同一个派生类,是否可以有一个函数调用所有这些实例并将它们重新分配给同一个 shared_ptr ?使用全局向量是可能的,但我想这是一种不好的做法^^ @SchneiderLoïc 不,将*sp_00 分配给*sp_01 实际上会调用Baseoperator=(Base const&amp;),这会将sp_01Base 部分重新分配给sp_00(通常,在您的情况下,m_a 将被重新分配,但不是仅在 Derived&lt;e0&gt;Derived&lt;e1&gt; 中的东西。 @SchneiderLoïc 参见rextester.com/TJEAV61599,重新分配*pxa = *pxb 时,仅重新分配Xx_y_ 属性保留为1,即使Y&lt;1&gt;Y&lt;2&gt; 相似(但它们不是同一类!)。在这种情况下,尝试将 pxa 转换为 Y&lt;2&gt;* 甚至会失败 - 运行时类型不会随分配而改变。【参考方案2】:

您可以动态转换原始指针;像

Derived* t = dymanic_cast<Derived*>(sp_00.get())

如果不能强制转换,你会得到 NULL,如果可以,你会得到一个有效的指针。也就是说,这需要内置 RTTI,这会使您的二进制文件变大,而完全需要这样做表明您的设计不正确。

【讨论】:

以上是关于将共享指针的派生类切换到基类的主要内容,如果未能解决你的问题,请参考以下文章

如何将此“命令处理程序映射”从派生类重构为基类?

在派生对象中的“this”指针上使用 static_cast 到基类的问题

基类指针和派生类指针

基类指针和派生类指针

为啥我的性能慢到爬行我将方法移动到基类中?

Pybind - 使用指向派生类的共享指针调用函数