By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,495 Members | 1,208 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,495 IT Pros & Developers. It's quick & easy.

C++ and Interfaces, Multiple Inheritance or Composition?

P: n/a
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
Share this Question
Share on Google+
9 Replies


P: n/a
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

P: n/a
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

P: n/a
Hi,

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

-vs_p...

Jul 23 '05 #4

P: n/a
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

P: n/a
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

P: n/a
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

P: n/a
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

P: n/a
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

P: n/a
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 discussion thread is closed

Replies have been disabled for this discussion.