A Request for Simple C++ Composition vs. Inheritance Examples
To expand a little on @jeremy-friesner's answer (and mostly reuse his code), a lot of the time composition is implemented using more classes than that. Essentially the Legs and Arms classes would be implementations of an interface. This makes it easy to inject those dependencies and, hence, mock/stub them out when unit testing the composite object. Then you'd have something like (ignoring virtual destructor...) :
class Walker // interface
{
public:
virtual void Walk() = 0;
}
class Legs : public Walker
{
public:
void Walk() {... code for walking around goes here...}
}
class Grabber // Interface
{
public:
virtual void GrabThings() = 0;
}
class Arms : public Grabber
{
public:
void GrabThings() {... code for grabbing things goes here...}
}
class InheritanceRobot : public Legs, public Arms
{
public:
// Walk() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot
{
public:
CompositionRobot(Walker& walker, Grabber& grabber)
: legs(walker),
arms(grabber)
{}
void Walk() {legs.Walk();}
void GrabThings() {arms.GrabThings();}
private:
Walker& legs;
Grabber& arms;
};
So the actual implementation used for legs and arms could be set at run-time instead of compile time.
As an aside, I only wrote this as an answer, rather than a comment on Jeremy's answer, to benefit from the code formatting so, if you feel like up-voting it, please do Jeremy's too.
HTH
UPDATE Sep 14, 2021:
One thing I've noticed in this answer is that I've conflated composition and aggregation. In composition, if the parent object ceases to exist, then so does the child object whereas, in aggregation, the child objects may exist after the parent is destroyed. The description I've given, where references to instances of the child objects are passed in the CompositionRobot constructor implies an aggregation relationship rather than composition. However, if you were to use std::unique_ptr() when defining the parameters and creating the objects, and std::move() when they're stored in the constructor of CompositionRobot, the effect would be much the same as in Jeremy's answer where the objects (rather than a pointer or a reference to them) are defined as class members.
Sure, why not? Since I like robots, let's make a robot that can walk around and grab things. We'll make one robot using inheritance, and another robot using composition:
class Legs
{
public:
void WalkAround() {... code for walking around goes here...}
};
class Arms
{
public:
void GrabThings() {... code for grabbing things goes here...}
};
class InheritanceRobot : public Legs, public Arms
{
public:
// WalkAround() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot
{
public:
void WalkAround() {legs.WalkAround();}
void GrabThings() {arms.GrabThings();}
private:
Legs legs;
Arms arms;
};
Note that at least for this example, the CompositionRobot
is usually considered to be the better approach, since inheritance implies an is-a
relationship, and a robot isn't a particular kind of Arms
and a robot isn't a particular kind of Legs
(rather a robot has-arms
and has-legs
).