C++ and Interfaces, Multiple Inheritance or Composition?

C

Code4u

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
 
R

raxitsheth

I think Composition is too ugly and Complicated.
You can use MI approach, using Pure Virtual Function.
It will Make Easy your work....
 
G

Ganesh

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
 
L

Laurens

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
 
L

Laurens

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
 
K

Kai-Uwe Bux

Ganesh said:
I would say prefer composition. Use inheritance only if isa
relationship holds hood. (other wise has a is better).

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
 
J

Jay Nabonne

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
 
K

Kai-Uwe Bux

Jay said:
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
 
B

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top