kinar
kinar

Reputation: 502

Need help restructuring and/or refacoring this c++ code concerning inheritance between multiple classes

OK, Not sure on the terminology for what I'm trying to do so edits are very welcome.

I've got code such as this:

class CBaseItemTracker
{
public:
    CBaseItemTracker();
    void IncrementCount() { ++count; }

protected:
    int count;
};

class CDerivedItemTracker : CBaseItemTracker
{
public:
    CDerivedItemTracker();
    void Print() { printf("something:%d\ncount:%d\n", something, count); }

private:
    int something;
};

class CBaseClass
{
public:
    BaseClass();

private:
    CItemTracker item;
};

class CDerivedClass : CBaseClass
{
public:
    CDerivedClass();
    CDerivedItemTracker GetDerivedItem();
private:
    CDerivedItemTracker derived_item;
};

What I would like to accomplish is that CBaseClass::item and CDerivedClass::derived_item are actually the same object such that:

  1. Code within CBaseClass can update item
  2. Code within CDerivedClass can update derived_item (and therby also update item)
  3. Code external to CDerivedClass can retrieve a CDerivedItemTracker object that includes the item variable.

I feel like I am trying to do something that is fundamentally wrong but I'm not seeing a good way to resolve it.

Upvotes: 1

Views: 173

Answers (1)

Christophe
Christophe

Reputation: 73386

Your goal is that code within CBaseClass can update base member item and code within CDerivedClass can update members derived_item as well as item.

Is it a member access problem ?

The first two constraints are the basics of inhertance.

The only problem in your code is that you have item member as private. This means that the derived classes won't have access to it. Just change it to be protected instead:

class CBaseClass
{
public:
    BaseClass();
protected:       //<===== derived classes will have direct acess to this member
    CItemTracker item;
};
class CDerivedClass : CBaseClass
{
public:
    CDerivedClass();
    CDerivedItemTracker GetDerivedItem();
private:    // you can leave it private, but if further dirved classe need access
            // make it protected as well, but then you should consider public inheritance 
            // so that the further derived also see the item. 
    CDerivedItemTracker derived_item;
};

Now the derived class sees both itmes. In GetDerivedItem() you can choose which item to return. But item and derived_item will remain unrelated, as your class definitions define two distinct members.

Or is it about sharing a single (derived) item ?

The third requirement makes the things a little bit more complex. I'm not 100% sure of your intentions. But if the intention is to extend in a derived class an item of the base class, this wont be possible as such, due to C++ standard constraints on the object layout.

Fortunately there are two main alternatives: templates or dynamic member.

Template approach

The template approach should be considered if at compile time you do already make the choice of the member type.

template <class myitem>
class CBase
{
public:
    CBase() {}
protected:
    myitem item; 
};
template <class myitem>
class CDerived : CBase<myitem>
{
public:
    CDerived() {}
    myitem GetDerivedItem() { return item; }
};

You can use this as follows:

CBase<CBaseItem> b; 
CDerived<CDerivedItem> d;   
d.GetDerivedItem().Print(); 

The principle is that for object d in CBase would have a CDerivedItem item, even if you would only access to the subset of its public CBaseItem members.

Dynamic approach

The dynamic approach doesn't use templates, but creates the item during the construction.

Here I present a simple implementation based on references, taking the luxury of some unused members. A more space efficient pointer based variant with dynamic allocation of the item should be prefered whenever space is critical.

class CBase
{
public:
    CBase() : item(base_item) {}    // when lonesome object: uses its own item 

protected:
    CBase(CBaseItem &itm) : item(itm) {}  // when subovject of a derived class
    CBaseItem &item;
private: 
    CBaseItem base_item;            // used only for non derived CBase object.  
};
class CDerived : CBase
{
public:
    CDerived() : CBase(derived_item) {} // Tell the base class that there's already an item to be used. 
    CDerivedItem GetDerivedItem() { return derived_item; }
         // we can use item as well: it'll be the CBaseItem subobject of derived_item
private:
    CDerivedItem derived_item;
};

ATTENTION: for this to work you need public inhertance between item classes ( class CDerivedItem : public CBaseItem)

The difficulty here, is that base classes are constructed first. So you have to provide to the base class constructor the item reference that it has to use. If inadvertently you'd forget, the base would think it is a free object and will use a separate item. THis is why I'd suggest to use the template approach if possible.

Upvotes: 2

Related Questions