c++ - 在具有存储基成员的基类的派生类中使用/存储派生成员

标签 c++ c++11 inheritance smart-pointers raii

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

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

首先说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将对象输出到哑指针区,如果在不方便的地方出现异常,它可能会泄漏。

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 Derived 中的类构造函数,因为 unique_ptrmove d离开之前Derived进去看看。

我可以通过提供 protected 来找到解决方案访问 BaseMemberstatic_cast回到DerivedMemberDerived构造函数(即在 Base 构造函数完成之后),但这似乎是一种丑陋的方式来重新访问我们从手指间溜走的变量!

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

我认为:

  • 这是反模式和整体设计的症状 Base/Derived/BaseMember/DerivedMember系统不是好的做法。
  • 我错过了一个技巧,有一种干净的方法可以做到这一点,而不会弄乱智能指针并导致泄漏成为可能,也不会添加函数并暴露接口(interface)或过度转换。

这是一个很好的重用模式,还是我应该寻找其他地方?

扩展用例(编辑)

在核心库中,我有一个类 DataInterpreter显示数据的“某种解释”——可以是字符串、图像等。然后由 TextInterpreter 等继承。它呈现一个 string .

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

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

在应用程序(依赖于核心库)中,这些类被扩展(通过继承或组合,以更有意义的为准)。例如,如果应用程序是 WX GUI,我可能有 wxDataAggregatePanel其中包含 wxTextDisplayPanel (和其他人),所有这些都是 wxPanels .在这种情况下,wxTextDisplayPanel可能拥有 wxTextEntry并拥有或继承TextInterpreter并利用其关于 TextInterpreter 的知识用字符串填充文本框的具体方法。

最佳答案

你可以使用委托(delegate)构造函数:

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

关于c++ - 在具有存储基成员的基类的派生类中使用/存储派生成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35326858/

相关文章:

c++ - 使用 openmode ios_base::out 的 istringstream 有什么意义

c++ - 将右值传递给可能不使用它的函数

c++ - 除法为负股息,但四舍五入为负无穷大?

iphone - 在 viewDidLoad 中的子类之后, [super viewDidLoad] 被调用,但方法没有被执行

java - java中继承情况下的同步

c++ - 如何在 C++ 中检查输入是否为数字

c++ - 如何以 1 对 1 的方式将所有 64 位整数映射到不同的 64 位整数

c++ - 如何使用 WM_* 消息调整窗口大小

c++ - 获取指针和常规 int 的奇怪值

c++ - 从子对象的方法返回的奇怪值