I am trying to understand the syntactic difference between composition and inheritance in C++.
I'm hoping someone will provide two simple examples. One example of a class that uses composition and one of a class that uses inheritance.
Whereas inheritance derives one class from another, composition defines a class as the sum of its parts. Classes and objects created through inheritance are tightly coupled because changing the parent or superclass in an inheritance relationship risks breaking your code.
Inheritance is an "is-a" relationship. Composition is a "has-a". You do composition by having an instance of another class C as a field of your class, instead of extending C . A good example where composition would've been a lot better than inheritance is java.
The main difference between inheritance and composition is in the relationship between objects. Inheritance: “is a.” E.g. The car is a vehicle. Composition: “has a.” E.g. The car has a steering wheel.
One more benefit of composition over inheritance is testing scope. Unit testing is easy in composition because we know what all methods we are using from another class. We can mock it up for testing whereas in inheritance we depend heavily on superclass and don't know what all methods of superclass will be used.
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
).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With