Using/storing 派生 class 中的派生成员,基 class 存储基成员
Using/storing derived member in derived class with base class that stores base member
我经常遇到的情况是有一组 classes,Base
和 Derived
,其中 Base
class 拥有所有权base-class 成员 BaseMember
的,并且 Derived
class 具有指向同一对象的引用或指针,但作为 DerivedMember
.
例如,一个 UI 面板 class 包含具有某些特殊控制功能的特定类型控件的特定实例,继承自包含一个通用 class通用控制,具有通用控制功能。
先说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
}
使用智能指针(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
离开 move
d 之后 Derived
才进去看看。
我可以看到一个解决方案,即提供对 BaseMember
的 protected
访问权限,并在 Derived
构造函数中 static_cast
返回 DerivedMember
(即在 Base
构造函数完成后),但这似乎是一种丑陋的方式来重新访问我们从手指间溜走的变量!
另一种方式是 Base
的每个继承者都拥有该指针,而 base 只获得一个哑指针。在这种情况下,Base
析构函数无法访问该成员,因为它已经不存在了。它还会不必要地重复所有权逻辑。
我认为:
- 这是反模式的症状,整个
Base
/Derived
/BaseMember
/DerivedMember
系统的设计不是好的做法。
- 我错过了一个技巧,有一种干净的方法可以做到这一点,而不会弄乱智能指针并导致泄漏成为可能,也不会添加函数并暴露接口或过度转换。
这是一个很好的重复使用模式,还是我应该去别处看看?
扩展用例(编辑)
在核心库中,我有一个 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
};
我经常遇到的情况是有一组 classes,Base
和 Derived
,其中 Base
class 拥有所有权base-class 成员 BaseMember
的,并且 Derived
class 具有指向同一对象的引用或指针,但作为 DerivedMember
.
例如,一个 UI 面板 class 包含具有某些特殊控制功能的特定类型控件的特定实例,继承自包含一个通用 class通用控制,具有通用控制功能。
先说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
}
使用智能指针(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
离开 move
d 之后 Derived
才进去看看。
我可以看到一个解决方案,即提供对 BaseMember
的 protected
访问权限,并在 Derived
构造函数中 static_cast
返回 DerivedMember
(即在 Base
构造函数完成后),但这似乎是一种丑陋的方式来重新访问我们从手指间溜走的变量!
另一种方式是 Base
的每个继承者都拥有该指针,而 base 只获得一个哑指针。在这种情况下,Base
析构函数无法访问该成员,因为它已经不存在了。它还会不必要地重复所有权逻辑。
我认为:
- 这是反模式的症状,整个
Base
/Derived
/BaseMember
/DerivedMember
系统的设计不是好的做法。 - 我错过了一个技巧,有一种干净的方法可以做到这一点,而不会弄乱智能指针并导致泄漏成为可能,也不会添加函数并暴露接口或过度转换。
这是一个很好的重复使用模式,还是我应该去别处看看?
扩展用例(编辑)
在核心库中,我有一个 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
};