A situation I often come up against is having a set of classes, Base
and Derived
, where the Base
class has ownership of a base-class member BaseMember
, and the Derived
class has a reference or pointer to the same object, but as a DerivedMember
.
For example, a UI panel class that contains a specific instance of a certain type of control with some special-control functions, inheriting from a general class that contains a general control and has general-control functions.
First, say that BaseMember
is inherited by DerivedMemeber
.
Without using smart pointers, I might do something like this:
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
}
With smart pointers (C++11 and up, specifically, I don't really care about older C++ in this case), I am tempted to do something like this and never let the Base/DerivedMember
object out into dumb-pointer-land where it could leak if there was an exception somewhere inconvenient.
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
}
As I noted there, you can't "steal a peek" at the DerivedMember
class as it comes in to the Derived
constructor, because the unique_ptr
is move
d away before Derived
gets a look in.
I can see a solution in providing a protected
access to the BaseMember
and static_cast
ing back to DerivedMember
in the Derived
constructor (i.e. after the Base
constructor is done), but this seems an ugly way to get access back to a variable we let slip though our fingers!
Another way could be each inheritor of Base
owns the pointer, and base just gets a dumb pointer. In this case, the Base
destructor doesn't get access to the member, as it's already gone. Also it would duplicate the ownership logic needlessly.
I think either:
Base
/Derived
/BaseMember
/DerivedMember
system is not good practice.Is this a good pattern for re-use, or should I look elsewhere?
In a core library, I have a class DataInterpreter
which shows "some interpretation" of data - could be a string, an image, etc. This is then inherited by, amongst others, TextInterpreter
which presents a string
.
I then have a DataDisplayPanel
class which represents a piece of UI for displaying in an abstract sense. Exactly what is in this panel will depend on the interpreter used: a TextInterpreter
should get a text entry field and say a button to set some text display option, and that is handled in TextDisplayPanel
, which has "special" knowledge of the text aspect of the interpreter.
There is then a DataAggregatePanel
which combines a number of DataDisplayPanels
and provides some global settings that affect all displays (via virtual functions), and manages the panels in a std::vector<std::unique_ptr<DataDisplayPanel> >
. This aggregate class doesn't deal with any of the derived classes at all, any functions would be polymorphic and defined in the base.
In the application (which depends on the core library), these classes are extended (by inheritance or composition, whichever makes more sense). For example, if the application is a WX GUI, I might have wxDataAggregatePanel
which contains wxTextDisplayPanel
(and others), all of which are wxPanels
. In this case, wxTextDisplayPanel
might own a wxTextEntry
and either own or inherit TextInterpreter
and use its knowledge of the TextInterpreter
's specific methods to fill the text box with a string.
A base class's private members are never accessible directly from a derived class, but can be accessed through calls to the public and protected members of the base class.
In C++, a derived class object can be assigned to a base class object, but the other way is not possible.
No. A reference to a derived class must actually refer to an instance of the derived class (or null).
Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.
You may use delegating constructor:
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
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With