473,324 Members | 2,581 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,324 software developers and data experts.

C++ and Interfaces, Multiple Inheritance or Composition?

My colleagues and I have been discussing techniques for implementing
interfaces in C++. We're looking for a mechanism similar to COM's
QueryInterface, in which a certain types of objects can be queried, at
run-time, for a particular interface and if it is supported, a pointer
or reference to that interface passed to the caller. Two possible
implementations came up, multiple inheritance and composition. Using
MI the class multiply inherits from all the classes it needs to
implement interfaces for. With composition a class contains a
collection of pointers to objects implementing the required
interfaces.

Both solutions have a little ugliness. With MI the interfaces the
class supports is fixed at compile time, to test whether an object
supports the interface dynamic_cast<T>, where T is the class
implementing the interface, would be executed. Using composition, each
of the objects in the container implements an interface, which
contains a back pointer to the parent object. To get a specific
interface the parent object would implement a method which would scan
the interface collection for a particular interface and return it to
the caller.

What do you think is the better approach? Is there a better pattern?

TIA

Jul 23 '05 #1
9 2280
I think Composition is too ugly and Complicated.
You can use MI approach, using Pure Virtual Function.
It will Make Easy your work....

Jul 23 '05 #2
I would say prefer composition. Use inheritance only if isa
relationship holds hood. (other wise has a is better).
From the Gang of four, design patterns book, we could clearly see, they mentioning, "favor composition over inheritance".

My 2 cents, any comments ?

-Ganesh

raxitsh...@gmail.com wrote: I think Composition is too ugly and Complicated.
You can use MI approach, using Pure Virtual Function.
It will Make Easy your work....


Jul 23 '05 #3
Hi,

If the classes are not related to each other then better compose it.

-vs_p...

Jul 23 '05 #4
As long as the interfaces only contain pure virtual functions and you'll
only use dynamic_cast to obtain pointers to instances, then I say go for
MI. You'll still have the flexibility to remove interfaces later on,
without breaking existing code.
Regards
-Laurens
Jul 23 '05 #5
You can also compose your class of multiple implementations of your
interfaces and forward the calls to the implementations:

class Alice {
public:
virtual void aliceMethod() = 0;
};

class Bob {
public:
virtual void bobMethod() = 0;
};

class AliceBob: public Alice, public Bob {
public:
virtual void aliceMethod() {
m_alice->aliceMethod();
}
virtual void bobMethod() {
m_bob->bobMethod();
}
private:
Alice* m_alice;
Bob* m_bob;
};
Regards
-Laurens
Jul 23 '05 #6
Ganesh wrote:
I would say prefer composition. Use inheritance only if isa
relationship holds hood. (other wise has a is better).
From the Gang of four, design patterns book, we could clearly see, they

mentioning, "favor composition over inheritance".

My 2 cents, any comments ?


Yes,
Your advice is based on fairly general principles and would apply verbatim
to any question about whether to use inheritance or composition. Whenever
general principles are invoked, I feel that the specific problem might be
forgotten. Here is some quickly whipped code illustrating the MI approach,
and I have the feeling that this can be done in a very clean way. I would
appreciate some code using composition for a comparison.

#include <iostream>
#include <string>

// we use a universal base class:
// WARNING: [only needed for the clone method]
// this approach can yield diamond situations if
// multiple inheritance is used later on.
class Base {
public:

virtual
~Base ( void ) {};

};
// interfaces are special, they do not inherit from Base
// WARNING: [this is imperative to avoid diamonds]
class IF_clone {
public:

virtual
Base* clone ( void ) const = 0;

virtual
~IF_clone ( void ) {};

};

class IF_name {
public:

virtual
std::string name ( void ) const = 0;

virtual
~IF_name ( void ) {}
};
// the interface cast:
template < typename IF,
typename Any >
IF* interface_cast ( Any * p ) {
return( dynamic_cast< IF* >( p ) );
}
// the application class hierarchy:

// class implements_interface< IF >

class A : public Base, public IF_clone {
public:

A* clone ( void ) const {
return( new A ( *this ) );
}

virtual ~A ( void ) {}

};

// class B implements both interfaces:

class B : public Base, public IF_clone, public IF_name {
public:

B* clone ( void ) const {
return( new B ( *this ) );
}

std::string name ( void ) const {
return( std::string( "b" ) );
}

virtual ~B ( void ) {}

};
int main ( void ) {
class Base * a = new A;
class Base * b = new B;
class Base * c = interface_cast< IF_clone >(b)->clone();
std::cout << ( interface_cast< IF_clone >( a ) ? true : false )
<< " "
<< ( interface_cast< IF_name >( a ) ? true : false )
<< "\n"
<< ( interface_cast< IF_clone >( b ) ? true : false )
<< " "
<< ( interface_cast< IF_name >( b ) ? true : false )
<< "\n"
<< ( interface_cast< IF_clone >( c ) ? true : false )
<< " "
<< ( interface_cast< IF_clone >( c ) ? true : false )
<< "\n";
}
Seems to be fairly straight forward.
Best

Kai-Uwe Bux
Jul 23 '05 #7
On Fri, 13 May 2005 05:56:57 -0400, Kai-Uwe Bux wrote:
Ganesh wrote:
One problem. Let's say you had this:
// interfaces are special, they do not inherit from Base
// WARNING: [this is imperative to avoid diamonds]
class IF_clone {
public:

virtual
Base* clone ( void ) const = 0;
// We have a function here with a common name.
virtual void SomeCommonlyNamedFunction() = 0;

virtual
~IF_clone ( void ) {};

};

class IF_name {
public:

virtual
std::string name ( void ) const = 0;

// We have a function here with the same common name.
virtual void SomeCommonlyNamedFunction() = 0;
virtual
~IF_name ( void ) {}
};
// the application class hierarchy:

Assuming the implementations need to be different for the two
interface methods, how would you implement class B below?
// class B implements both interfaces:

class B : public Base, public IF_clone, public IF_name {
public:

B* clone ( void ) const {
return( new B ( *this ) );
}

std::string name ( void ) const {
return( std::string( "b" ) );
}

virtual ~B ( void ) {}

};


You could force your interfaces to have function names unique per
interface, but it seems like a potential nightmare down the road when you
have a large number of interfaces.

- Jay

Jul 23 '05 #8
Jay Nabonne wrote:
On Fri, 13 May 2005 05:56:57 -0400, Kai-Uwe Bux wrote:
Ganesh wrote:


One problem. Let's say you had this:
// interfaces are special, they do not inherit from Base
// WARNING: [this is imperative to avoid diamonds]
class IF_clone {
public:

virtual
Base* clone ( void ) const = 0;


// We have a function here with a common name.
virtual void SomeCommonlyNamedFunction() = 0;

virtual
~IF_clone ( void ) {};

};

class IF_name {
public:

virtual
std::string name ( void ) const = 0;


// We have a function here with the same common name.
virtual void SomeCommonlyNamedFunction() = 0;
virtual
~IF_name ( void ) {}
};
// the application class hierarchy:


Assuming the implementations need to be different for the two
interface methods, how would you implement class B below?
// class B implements both interfaces:

class B : public Base, public IF_clone, public IF_name {
public:

B* clone ( void ) const {
return( new B ( *this ) );
}

std::string name ( void ) const {
return( std::string( "b" ) );
}

virtual ~B ( void ) {}

};


Thanks, that's a very good point. Admittedly, I am not entirely satisfied
with what I have so far (it appears to be a little cludgy). But here is a
proposal based on disambiguation by signature. The problem that you pointed
out arises because there can be at most one method with a given name in any
class, unless their signatures differ. The idiom, below uses that:

#include <iostream>
#include <string>
#include <cassert>

// we use a universal base class:
// WARNING: [only needed for the clone method]
// this approach can yield diamond situations if
// multiple inheritance is used later on.
class Base {
public:

virtual
~Base ( void ) {};

};
// interfaces are special, they do not inherit from Base
// WARNING: [this is imperative to avoid diamonds]
// IDIOM: the last argument in each method is
// IF_<name> const & dummy = IF_<name>()
// one could have a macro doing that.
class IF_clone {
public:

IF_clone ( void ) {}

virtual
Base* clone ( IF_clone const & dummy = IF_clone() ) const {
assert( false );
};

virtual
~IF_clone ( void ) {};

};

class IF_name {
public:

IF_name ( void ) {}

virtual
std::string name ( IF_name const & dummy = IF_name() ) const {
assert( false );
};

virtual
~IF_name ( void ) {}
};

class IF_lib_name {
public:

IF_lib_name ( void ) {}

virtual
std::string name ( IF_lib_name const & dummy = IF_lib_name() ) const {
assert( false );
};

virtual
~IF_lib_name ( void ){}

};

// the interface cast:
template < typename IF,
typename Any >
IF* interface_cast ( Any * p ) {
return( dynamic_cast< IF* >( p ) );
}
// the application class hierarchy:

// class implements_interface< IF >

class A : public Base, public IF_clone {
public:

A* clone ( IF_clone const & dummy = IF_clone() ) const {
return( new A ( *this ) );
}

virtual ~A ( void ) {}

};

// class B implements both interfaces:

class B : public Base,
public IF_clone,
public IF_name,
public IF_lib_name {
public:

B* clone ( IF_clone const & dummy = IF_clone() ) const {
return( new B ( *this ) );
}

std::string name ( IF_name const & dummy = IF_name() ) const {
return( std::string( "b" ) );
}

std::string name ( IF_lib_name const & dummy = IF_lib_name() ) const {
return( std::string( "lib_B" ) );
}

virtual ~B ( void ) {}

};
int main ( void ) {
class Base * a = new A;
class Base * b = new B;
class Base * c = interface_cast< IF_clone >(b)->clone();
std::cout << ( interface_cast< IF_clone >( a ) ? true : false )
<< " "
<< ( interface_cast< IF_name >( a ) ? true : false )
<< "\n"
<< ( interface_cast< IF_clone >( b ) ? true : false )
<< " "
<< ( interface_cast< IF_name >( b ) ? true : false )
<< "\n"
<< ( interface_cast< IF_clone >( c ) ? true : false )
<< " "
<< ( interface_cast< IF_clone >( c ) ? true : false )
<< "\n";
std::cout << interface_cast< IF_name >(c)->name()
<< " "
<< interface_cast< IF_lib_name >(c)->name()
<< "\n";
}
As you can see, the client code didn't change at all.
Best

Kai-Uwe Bux
Jul 23 '05 #9
ben
If you are sticking to a single protocol like IUnknow where whether MI or
composition is kept away from the user, then better make it an open decision
for specific classes.

ben
Jul 23 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

21
by: Franco Gustavo | last post by:
Hi, Please help me to understand this, because I don't see what I'm missing. I was reading a lot of examples on Internet that explain that C# doesn't implement multiple inheritance it...
9
by: Sean Kirkpatrick | last post by:
To my eye, there doesn't seem to be a whole lot of difference between the two of them from a functional point of view. Can someone give me a good explanation of why one vs the other? Sean
60
by: Shawnk | last post by:
Some Sr. colleges and I have had an on going discussion relative to when and if C# will ever support 'true' multiple inheritance. Relevant to this, I wanted to query the C# community (the...
13
by: interec | last post by:
I have some code in Java that I need to translate into C++. My Java code defines a class hierarchy as follows: // interface IA public interface IA { public abstract void doA(); } //...
18
by: GD | last post by:
Please remove ability to multiple inheritance in Python 3000. Multiple inheritance is bad for design, rarely used and contains many problems for usual users. Every program can be designed only...
4
by: sip.address | last post by:
Hi there, When creating interfaces and implementations, the usual thing is doing somethign like class Interface { public: virtual void f() = 0; virtual void g() = 0; };
2
by: Immortal Nephi | last post by:
You may have heard diamond shape. You create one base class. One base class has member functions and member variables. You create two derived classes. All member functions and member variables...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.