Using/storing 派生 class 中的派生成员,基 class 存储基成员

Using/storing derived member in derived class with base class that stores base member

我经常遇到的情况是有一组 classes,BaseDerived,其中 Base class 拥有所有权base-class 成员 BaseMember 的,并且 Derived class 具有指向同一对象的引用或指针,但作为 DerivedMember.

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

先说BaseMemberDerivedMemeber继承。

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

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
}

使用智能指针(C++11 及更高版本,具体来说,在这种情况下我并不真正关心旧的 C++),我很想做这样的事情并且从不让 Base/DerivedMember 对象如果在不方便的地方出现异常,它可能会泄漏到 dumb-pointer-land 中。

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
}

正如我在那里指出的那样,您不能在 DerivedMember class 处 "steal a peek" 因为它进入 Derived 构造函数,因为 unique_ptr 离开 moved 之后 Derived 才进去看看。

我可以看到一个解决方案,即提供对 BaseMemberprotected 访问权限,并在 Derived 构造函数中 static_cast 返回 DerivedMember(即在 Base 构造函数完成后),但这似乎是一种丑陋的方式来重新访问我们从手指间溜走的变量!

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

我认为:

这是一个很好的重复使用模式,还是我应该去别处看看?

扩展用例(编辑)

在核心库中,我有一个 class DataInterpreter 显示 "some interpretation" 的数据——可以是字符串、图像等。其他 TextInterpreter 表示 string.

然后我有一个DataDisplayPanel class代表一块UI抽象的展示。该面板中的确切内容将取决于所使用的解释器:TextInterpreter 应该得到一个文本输入字段并说出一个按钮来设置一些文本显示选项,这是在 TextDisplayPanel 中处理的,它有 "special" 解释器文本方面的知识。

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

在应用程序(取决于核心库)中,这些 classes 被扩展(通过继承或组合,以更有意义的为准)。例如,如果应用程序是 WX GUI,我可能有 wxDataAggregatePanel,其中包含 wxTextDisplayPanel(和其他),所有这些都是 wxPanels。在这种情况下,wxTextDisplayPanel 可能拥有一个 wxTextEntry 并且拥有或继承 TextInterpreter 并使用它对 TextInterpreter 的特定方法的了解用字符串填充文本框.

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

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
};