Sign in to follow this  
jorgander

C++ diamond inheritance problem

Recommended Posts

I'm familiar with inheritance, I'm just not sure if what I want to do is possible. An example probably best describes it:
class Base
{
public:
    Base * parent;
    virtual void Func()
    {
        // do some stuff
        parent->Func();
    }
};
class Derived1 : public virtual Base
{
public:
    virtual void Func()
    {
        // do some stuff
        Base::Func();
    }
};
class Derived2 : public virtual Base
{
public:
    virtual void Func()
    {
        // do some stuff
        Base::Func();
    }
};
class DerivedBoth : public Derived1, public Derived2
{
public:
    virtual void Func()
    {
        // do some stuff
        Derived1::Func();
        Derived2::Func();
        Base::Func();
    }
};

- If Derived1::Func is called, Base::Func should also get called (as is shown) - If Derived2::Func is called, Base::Func should also get called (as is shown) - If DerivedBoth::Func is called, all of Derived1::Func, Derived2::Func, and Base::Func should get called exactly once (as is shown). Of course, as it stands now Base::Func will get called 3 times per call to DerivedBoth::Func, where I would like Base::Func to get called exacly once per call to Derived1::Func, Derived2::Func, or DerivedBoth::Func. I could accomplish it the following way, but I'd like to avoid using such hacks:
class Base
{
public:
    Base * parent;
    virtual void Func(bool Unused = true)
    {
        // do some stuff
        parent->Func();
    }
};
class Derived1 : public virtual Base
{
public:
    virtual void Func(bool CallBase = true)
    {
        // do some stuff
        if ( CallBase ) Base::Func();
    }
};
class Derived2 : public virtual Base
{
public:
    virtual void Func(bool CallBase = true)
    {
        // do some stuff
        if ( CallBase ) Base::Func();
    }
};
class DerivedBoth : public Derived1, public Derived2
{
public:
    virtual void Func(bool Unused = true)
    {
        // do some stuff
        Derived1::Func(false);
        Derived2::Func(false);
        Base::Func();
    }
};

Share this post


Link to post
Share on other sites
Quote:
Original post by jorgander
Of course, as it stands now Base::Func will get called 3 times per call to DerivedBoth::Func, where I would like Base::Func to get called exacly once per call to Derived1::Func, Derived2::Func, or DerivedBoth::Func.

you can check in Base::Func if it was already called. Something like this:

class Base
{
public:
Base * parent;
bool funcCalled;
virtual void Func()
{
if(!funcCalled)
{
// do some stuff
parent->Func();
funcCalled = true;
}
}
};
class Derived1 : public virtual Base
{
public:
virtual void Func()
{
// do some stuff
Base::Func();
}
};
class Derived2 : public virtual Base
{
public:
virtual void Func()
{
// do some stuff
Base::Func();
}
};
class DerivedBoth : public Derived1, public Derived2
{
public:
virtual void Func()
{
Base::funcCalled = false;
// do some stuff
Derived1::Func();
Derived2::Func();
Base::Func();
}
};

Share this post


Link to post
Share on other sites
I could do that, and all things considered it is probably better than my example of adding a boolean parameter, since the same boolean member variable could be used for any number of functions that must follow the same paradigm.

I was hoping to achieve the functionality without adding any overhead, be it extra member variables or function parameters. Exactly like Base::Base() is called only once per DerivedBoth::DerivedBoth(), I'd like my own definable base function to be called once per derived function. Perhaps it is not possible though.

Share this post


Link to post
Share on other sites
Hi,

don't really know why you would want to do this, but it looks a lot like a tree structure with nodes you are attemping. if you don't want overhead than c++ is not the language for you.

if I was you a simple solution is to have your base class have children of type base. then change your derivedboth class to derive from the base class.

class CBase
{
public:
addChild( const CBase& child ); // create some code to put the child in a list
};

class CA : public CBase
{
public:
}

class CB : public CBase
{
public:
}

class CAB : public CBase
{
public:
}

now class CAB using the method addChild can add CA and CB. Now you can have any function you want and just traverse the children recursivly.

kind regards,
Peter Wraae Marino (www.axiom3d.dk and www.spilfirma.dk)

Share this post


Link to post
Share on other sites
Quote:
Original post by jorgander
I was hoping to achieve the functionality without adding any overhead, be it extra member variables or function parameters. Exactly like Base::Base() is called only once per DerivedBoth::DerivedBoth(), I'd like my own definable base function to be called once per derived function. Perhaps it is not possible though.


Well, you can get away with not adding any new members. However, it would require RTTI. I'm not sure which one of those woould be better, but here's a proof of concept (it should compile & run without any changes - it worked for me):

#include <iostream>
#include <string>
#include <typeinfo>

using namespace std;

class Base
{
public:
virtual void Func()
{
cout << "Base::Func()" << endl;
}
};

class Derived1 : public virtual Base
{
public:
virtual void Func()
{
cout << "Derived1::Func()" << endl;
if(typeid(*this) == typeid(Derived1)) Base::Func();
}
};

class Derived2 : public virtual Base
{
public:
virtual void Func()
{
cout << "Derived2::Func()" << endl;
if(typeid(*this) == typeid(Derived2)) Base::Func();
}
};

class DerivedBoth : public Derived1, public Derived2
{
public:
virtual void Func()
{
cout << "DerivedBoth::Func()" << endl;
Derived1::Func();
Derived2::Func();
if(typeid(*this) == typeid(DerivedBoth)) Base::Func();
}
};

void run(Base & obj, string name)
{
cout << "======== " << name << endl;
obj.Func();
cout << "========" << endl;
}

int main()
{
Derived1 d1;
Derived2 d2;
DerivedBoth db;
run(d1, "Derived1 object");
run(d2, "Derived2 object");
run(db, "DerivedBoth object");
return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Crisium
Hi,

don't really know why you would want to do this, but it looks a lot like a tree structure with nodes you are attemping. if you don't want overhead than c++ is not the language for you.

...snip...


You are correct, they form a binary tree. I had not put it in the example, but Base also contains two Base * pointers to child nodes. To minimize overhead, traversal of the tree (and thus, calling its methods), is only heavily done during creation and destruction, and not during most of the life of the tree. This way, I get cleaner/reusable code without too much program slow down. I could explain the logic behind it and what exactly they are used for, but it would take a while and I'd be going somewhat off topic. But by all means, I am open to any critique and thanks for pointing it out.

Paulius Maruska:

Thanks for your suggestion. I'm not sure if I'll use it, but I hadn't thought of it and it is good to know of it.

Share this post


Link to post
Share on other sites

class Base
{
public:
Base * parent;
void Func(void)
{
DoFunc();
parent->Func();
}
private:
virtual void DoFunc(void)
{
}
};
class Derived1 : public virtual Base
{
private:
virtual void DoFunc(void)
{
// do some stuff
}
};
class Derived2 : public virtual Base
{
private:
virtual void DoFunc(void)
{
// do some stuff
}
};
class DerivedBoth : public Derived1, public Derived2
{
private:
virtual void DoFunc(void)
{
// do some stuff
Derived1::DoFunc();
Derived2::DoFunc();
}
};

Share this post


Link to post
Share on other sites
What you have there is an example of the Call Super antipattern, which is usually a clear sign of bad object oriented design.

There has already been a few suggestions on how to solve the initial problem. However, it could be better if you refactorize and change your design so that you won't need to call the super class' methods like that.

-Riku

Share this post


Link to post
Share on other sites
Thx for the replies. I implemented something similar to what SiCrane posted, which is also what riku's wiki suggested as a solution for the type of design problem I had. I'm sure I've used that method before, I have no idea why I did not think of it this time. Anyway, thanks again.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this