Sign in to follow this  
Wavesonics

C++ ABCs good practice question

Recommended Posts

So I'm using Abstract Base Classes as interfaces, and got a question for the OOP gurus out there. In my engine absolutely EVERYTHING inherits from a class called 'Element' which has certain function that allow it to function within my framework. But when I'm passing around objects of the Interface ABC the interface doesn't know about these functions. And I need to use these function. So should I put the 1 function I need in the interface even though that function as nothing to do with the purpose of the interface? Or maybe have a ABC of Element that all interface's inherit from so all Interfaces have those function that all classes have already defined?

Share this post


Link to post
Share on other sites
Just discovered this doesn't even work.

Just because a parent class of a certain class implements a certain function, if the child class inherits from an interface which declares that function pure virtual it won't use the base classes implementation and will throw a compilation error.

So what should I do? reinterpret_cast when I need that function?

Share this post


Link to post
Share on other sites
You might need to use the virtual keyword when inheriting the interface:

class Element
{
public:
virtual void SomeFunction() = 0;
};

class MoreSpecificInterface : public virtual Element
{
public:
virtual void SpecificFunction() = 0;
};

class AnotherInterface : public virtual Element
{
public:
virtual void AnotherFunction() = 0;
};

class ConcreteClass : public virtual Element, public MoreSpecificInterface, public AnotherInterface
{
public:
virtual void SomeFunction()
{
}

virtual void SpecificFunction()
{
}

virtual void AnotherFunction()
{
}
};


The virtual keyword used when inheriting from Element says that there is only going to be one Element parent, and it will be shared among any classes that also virtually inherit the Element class. Otherwise, MoreSpecificInterface would have it's own Element parent, as would AnotherInterface, as well as ConcreteClass.

Share this post


Link to post
Share on other sites
Ok let me try to understand/clarify this:


class Element {
string getElementId() { return m_strElementId; }
};

class Interface {
virtual void someInterfaceFunc() = 0;
};

class SpecificSomething : public Element, public Interface {
void someInterfaceFunc() { // do something
}
};




Now when I pass around an object of 'SpecificSomething' as the 'Interface' type I still need to be able to use the functions from my base class like 'string getElementId()'.

Soooo... Would what you are saying solve this problem? B/c from my understand it would not i don't think.

Share this post


Link to post
Share on other sites
I'll leave the more in-depth answers to the gurus, but meanwhile I'll go out on a limb and say that:
Quote:
In my engine absolutely EVERYTHING inherits from a class called 'Element' which has certain function that allow it to function within my framework.
This is probably might be a bad idea, and...
Quote:
So should I put the 1 function I need in the interface even though that function as nothing to do with the purpose of the interface?
...no, you shouldn't.

You might find this article interesting. It's somewhat subjective and is focused more on generic components such as containers than on object hierarchies, but there are some points in the section titled 'Inheritance' that I think might be relevant.

Here is another interesting read that talks about some of the potential pitfalls of deep hierarchies.

There are some gotcha's as well that you have to watch out for when making use of inheritance and polymorphism. Furthermore, these sorts of errors can often go undetected and lead to undefined behavior (that's one of the reasons template programming gives me warm fuzzies - most of the errors happen at compile time ;). The thought of trying to manage a code base where every single class is part of the same hierarchy gives me the willies, but YMMV.

Again, I'm not a guru, but maybe the links I posted will at least give you some things to think about.

[Edited by - jyk on March 3, 2007 7:20:11 PM]

Share this post


Link to post
Share on other sites
Well having a base class that implements functionality required to function with in the engine is, as I understand it, how most engines work. Wild Magic and Source (Half-Life 2) both do this.

But i'll check out those articles, thanks for the reply.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wavesonics
Well having a base class that implements functionality required to function with in the engine is, as I understand it, how most engines work. Wild Magic and Source (Half-Life 2) both do this.
Well, I may have taken it too literally when you said 'everything' in your engine inherits from the Element class.

In any case, I'm sure that's a valid approach; I just wanted to point out that there are different ways of looking at the problem.

That said, my first statement that 'this is probably a bad idea' was probably too strong - I'll go back and edit it. However, I'll stand by my second statement regarding the addition of functions to the base class interface that are really only applicable for a particular derived class, as I think that definitely raises some red flags.

Share this post


Link to post
Share on other sites
Yes I agree with what you said about adding the function to the interface. And infact, I ended up doing a dynamic_cast and it was able to find the base class just fine so i guess it's rather a non-issue, but again thank you for your posts!

Share this post


Link to post
Share on other sites
Now the question is: why should a function that takes an Interface as a parameter expect to be able to use that Interface as an Element?

Case 1: There are Interfaces that are not Elements and the dynamic cast will not always work, which your code should be checking for to avoid dereferencing a null pointer.

Case 2: All Interfaces are Elements and the dynamic cast is completely redundant.

I think your engine falls under case 2.

If everything that implements one of your engine's interfaces should also implement element, you might want to consider making the interfaces themselves inherit from element. Now there's the issue of diamond inheritance...does your engine's design allow a single class to inherit from multiple interfaces? In that case, you'd also want to make your interfaces use virtual inheritance of the Element class (otherwise it doesn't matter).

Share this post


Link to post
Share on other sites
Quote:

So should I put the 1 function I need in the interface even though that function as nothing to do with the purpose of the interface?


If every inheritor of an interface will have this function, I fail to see how that function has nothing to do with the interface.

If you want a logical disconnect:

class Element {
string getElementId() { return m_strElementId; }
};

class Interface: public Element {
virtual void someInterfaceFunc() = 0;
};

class SpecificSomething : public Interface {
void someInterfaceFunc() { // do something
}
};



You can then reuse Element without Interface, and things tend to stay a little cleaner in the SpecificSomethings.

Share this post


Link to post
Share on other sites
You do bring up a good point and one I have been thinking about.

1. Everything that inherits from the interface WILL inherit from Element as well, just not directly. But Element is the eventual base class for everything.

I had been thinking about just making the interface a base class. But I do like what Telastyn suggested. I think it's the best compromise. Still need to investigate the matter a bit more.

But what is virtual inheritance?

Share this post


Link to post
Share on other sites
Quote:

But what is virtual inheritance?


(In practical terms:)

Not infrequently, multiple inheritance creates problems for compilers because they don't know which base/derived class in the multiple should be used for a given choice. So in C++ it was decided to allow it anyways, but require that the programmer explicitly specify which one to use. Using virtual inheritance says there might be different base/derived classes in the inheritance hierarchy and that you'll specify which to use.

At first glance this doesn't seem like a big deal, but in practice creates a bit more work and annoyance to the programmer. (like so many other things about C++)

Share this post


Link to post
Share on other sites
Ah thank you, and how do u specify which one to use?

Quote:

class C : virtual public A, public B;


Would A be the base class it would use?

[edit] AAAaaahhh... that wiki article cleared it up damn well, thanks so much! [/edit]

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