在派生类中使用/存储派生成员与存储基本成员的基类

Ind*_*oad 6 c++ inheritance smart-pointers raii c++11

我经常遇到的一种情况是拥有一组类,Base并且Derived,Base类具有基类成员的所有权BaseMember,并且Derived该类具有指向同一对象的引用或指针,但是作为一个DerivedMember.

例如,一个UI面板类,它包含具有某些特殊控制功能的特定类型控件的特定实例,继承自包含常规控件并具有通用控件功能的通用类.

首先,说BaseMember是继承的DerivedMemeber.

不使用智能指针,我可能会这样做:

class Base
{
protected:
    // receive ownership but only because we say so,
    // someone else can still try to delete as it's "just a pointer"
    Base(BaseMember* _bmember):
        bmember(_bmember)
    {}

public:
    virtual ~Base()
    {
        // perform an owner's duty
        delete bmember;
    }

    // functions that might be based on BaseMember + other base state
    void SetMemberId(....) 
    {
        bmember->SetId(baz);
    }

private:
    int baz;
    BaseMember* bmember; //owned, but not smartly
}

class Derived: public Base
{
public:
    Derived(DerivedMember* _dmember):
        Base(_dmember),
        dmember(_dmember)
    {}

    // functions that only make sense for Derived + Derived/Base state
    void SetDerivedFrobulation()
    {
        // only a DerivedMember has frobulation, so only
        // Derived allows users to access it
        dmember->setFrobulation(foo);
    }

private:
    int foo; // some state
    DerivedMember* dmember; // no ownership here
}
Run Code Online (Sandbox Code Playgroud)

使用智能指针(C++ 11及更高版本,特别是在这种情况下,我并不真正关心旧的C++),我很想做这样的事情,永远不要把Base/DerivedMember对象放到哑指针的地方,它可以如果在某处不方便的情况下发生异常泄漏.

class Base
{
protected:
    // receive ownership
    Base(std::unique_ptr<BaseMember> _member):
        member(std::move(_member))
    {}

    virtual ~Base()
    {}

public:
    // public access functions here as before

private:
    std::unique_ptr<BaseMember> member;
}

class Derived: public Base
{
public:
    // pass the ownership down by unique_ptr
    Derived(std::unique_ptr<DerivedMember> _dmember):
        Base(std::move(_dmember)),
        dmember(_dmember.get()) // _dmember is moved! SEGFAULT if access dmember later!
    {}

    // public access functions here as before

private:
    // handy handle to the derived class so we don't need to downcast the base (or even access it!)
    DerivedClass* dmember
}
Run Code Online (Sandbox Code Playgroud)

正如我在那里所指出的那样,当你DerivedMember进入Derived构造函数时,你无法在类中"偷看" ,因为在查看之前它unique_ptr已经move消失Derived了.

我可以提供看的解决方案protected来访问BaseMemberstatic_cast荷兰国际集团回DerivedMemberDerived构造函数(后即Base构造完成),但这似乎丑陋的方式来获得访问回到我们放过虽然我们的手指变!

另一种方式可能是Base拥有指针的每个继承者,而base只是获得一个哑指针.在这种情况下,Base析构函数无法访问该成员,因为它已经消失了.它也会不必要地复制所有权逻辑.

我想要么:

  • 这是反模式的症状,整个Base/ Derived/ BaseMember/ DerivedMember系统的设计不是好的做法.
  • 我错过了一个技巧,有一个干净的方法来做到这一点,而不会破坏智能指针,使泄漏成为可能或添加功能和暴露接口或铸造太多.

这是一种重复使用的好模式,还是应该在其他地方寻找?

扩展用例(EDIT)

在一个核心库中,我有一个类DataInterpreter,它显示了数据的"某种解释" - 可以是一个字符串,一个图像等.然后由其他人TextInterpreter提交一个string.

然后,我有一个DataDisplayPanel类,它代表一个用于在抽象意义上显示的UI.确切地说,这个面板中的内容将取决于所使用的解释器:a TextInterpreter应该获得一个文本输入字段并说一个按钮来设置一些文本显示选项,并且处理中TextDisplayPanel,它具有解释器文本方面的"特殊"知识.

然后有一个DataAggregatePanel组合了许多DataDisplayPanels并提供一些影响所有显示(通过虚拟功能)的全局设置,并管理a中的面板std::vector<std::unique_ptr<DataDisplayPanel> >.这个聚合类根本不处理任何派生类,任何函数都是多态的并且在基类中定义.

在应用程序(依赖于核心库)中,这些类被扩展(通过继承或组合,无论哪个更有意义).例如,如果应用程序是WX GUI,我可能wxDataAggregatePanel包含wxTextDisplayPanel(和其他),所有这些都是wxPanels.在这种情况下,wxTextDisplayPanel可能拥有a wxTextEntry或者拥有或继承TextInterpreter并使用其TextInterpreter特定方法的知识来用文本框填充文本框.

Jar*_*d42 5

您可以使用委托构造函数:

class Derived: public Base
{
public:

    Derived(std::unique_ptr<DerivedMember> _dmember):
        Derived(_dmember, _dmember.get())
    {}

    // public access functions here as before
private:
 Derived(std::unique_ptr<DerivedMember>& _dmember, DerivedMember* ptr):
        Base(std::move(_dmember)),
        dmember(ptr)
    {}
private:
    // handy handle to the derived class so we don't need to downcast the base (or even access it!)
    DerivedClass* dmember
};
Run Code Online (Sandbox Code Playgroud)