Chemistpp
Chemistpp

Reputation: 2056

Inherit or include? C++

I had a really long post on this and decided it can be summed up much shorter. Canonically speaking, is it better to include a data member inside of a class as opposed to inheriting it? I found I can implement identical functions either way, but don't really know what caveats I should be aware of.

Code example

#include "KClass.h"
class KPC : public KCharacter {
private:
    KClass MyClass;
};

versus

class KClass : public KCharacter {

};

class KPC : public KClass {


};

In the first example, anytime I needed something from the KClass data, I could access it via MyClass->

In the second class, class KPC would just directly access them since it would inherit the data members.

For specifics to my problem I guess I should detail the class' function.

D&D format. Each character has a class which would determine: weapon/armor proficiencies, bonus defenses, special ability i.e. defender has mark.

So to me, it made sense to inherit it. However, is class a more specific PC or is PC a specific kind of class? There are so many PCs in a game that aren't a specific class, actually class should inherit PC on that concept sense it's more 'specialized' form of a PC. So would I want to structure it in a way of KClass : KPC ?

It seemed easier to implement a Has-A at first, but now I'm second guessing it. Hence the reason why I'm here, asking this question.

Upvotes: 2

Views: 1080

Answers (4)

Pixelchemist
Pixelchemist

Reputation: 24956

Conceptual

The concept of classes and objects is usually used to model "real" things. But let's put the cart before the horse.

The transfer of the inheritance concept to the real world would be (like others said) an IS A-relation.

  • A TFT is a screen
  • A Fox is an Animal
  • ...

The composition is, in contrast, usually considerd as HAS A-relation.

  • A PC has a CPU
  • A knife has a blade
  • ...

So if you want to model the latter in object-oriented programming, make use of composition. In case of the former concept, use inheritance.

Examples

Composition > Inheritance

Examples always tend to come naturally to me. So I'll try to illustrate it a bit further. (No encapsulation here, sorry. ;))

Consider motorvehicles, respectively cars. The tend to have an engine, which has a specific sound.

struct Engine
{
  void sound (void) const { std::cout << "BROOOM" << std::endl; }
  void open_valve (void) { /* ... */ }
};

Engines also can perform certain engine-specific tasks.

Now we can have both specified options to include the engine into a car: inheritance or composition.

struct Car_A : public Engine { }; 

At the first moment, this seems appropriate. We don't need to reprovide sound() since a car (in the first approximation) just sounds like an engine.

Car_A a_car;
a_car.sound(); // mycar sounds like a car!

But the noise is not very realistic: No tread noise, no air draft. So we can just shadow the underlying method and define:

struct Car_A : public Engine 
{ 
  void sound (void) const 
  {
    std::cout << "tread noise + air draft" << std::endl;
    Engine::sound();
  }
}; 

We still have a slight problem.

a_car.open_valve(); // ?

The concept of valves is part of the engine but not part of the car but we can use this method on the car. The car has an engine but it isn't one. We could switch to private inheritance now but the method would still be present, although not accessible.

Another (less conceptual) issue can be seen when using pointers of the types:

Engine * pointer_to_engine(new Car_A); // works

An engine that actually is a car? "(Suspected) Engines" exhibiting car behaviour and vice versa? Well that doesn't look like the way to do things here.

Let's look at composition instead:

struct Car_B 
{ 
  void sound (void) const 
  { 
    std::cout << "tread noise + air draft" << std::endl;
    engine.sound(); 
  }
  void open_door (void) { /* ... */ }
  Engine engine;
}; 

That's how things are supposed to be: A car that has a[n] (member) engine that sounds like an engine and contributes to the sound of the car and no methods are present in the car that are not part of the concept of a car.

Car_B b_car;
b_car.sound(); // still sounds like a car!
b_car.engine.open_valve(); // meaningful for an engine!

Here we have a case where composition is superior.

  • The "real" situation is modeled.
  • All concepts keep their validity. (No unintended behaviour.)

Inheritance > Composition

Now we add another Concept in our example: a vehicle.

struct Wheel {};
struct Motorvehicle
{
  virtual void sound (void) const { engine.sound(); }
  Engine engine;
  std::vector<Wheel> wheels;
};

A motorvehicle is driven by an engine, so it knows to emmit engine sound. However, the abstract vehicle has no clue of how many wheels its concrete objects will have have (motorcycle? car?) or how its shape is formed, so it can't tell anything about tread noise and air draft.

This time we look at composition first (miracle miracle...):

struct Car_C
{
  void sound (void) const 
  { 
    std::cout << "tread noise + air draft" << std::endl;
    vehicle.sound();
  }
  Motorvehicle vehicle;
};

Looks legit, doesn't it?

Car_C c_car;
c_car.sound(); // correct sound!
c_car.vehicle.sound(); // what the hell is "the vehicle of a car"?
c_car.wheels.... // error the car has no wheels?!

"Pretending" that wheels are part of the car will require us to add an additional function for our car. If we use inheritance instead, this coherency comes from scratch.

struct Car_D 
  : public Motorvehicle
{
  void sound (void) const 
  { 
    std::cout << "tread noise + air draft" << std::endl;
    Motorvehicle::sound();
  }
};

The observable behaviour of Car_D is more like you would expect it to be.

Car_D d_car;
d_car.sound(); // correct sound!
d_car.wheels.[...] // valid, our car has wheels!

Conclusion

The consideration, whether to use inheritance or composition is not always as easy as in my examples but you should try to weight up and choose the concept that performas better in reflecting the desired behaviour. If the designated base class describes an abstract generalization of the derived class, this is a good hint for inheritance.

Upvotes: 1

Mats Petersson
Mats Petersson

Reputation: 129454

It really depends on what you are trying to do. Yes, both achieve mechanically similar things, but the rule is "is-a" or "has-a" for deciding which way to go.

If KPC really "is-a" form of KClass, then you should use inheritance. This means that you are looking to solve a polymorphic problem - you have several items that are similar:

class AeroPlaneBase
{
  ...
};

class JetPlane : public AeroPlaneBase
{
  ...
};

class PropellerPlane : public AeroPlaneBase
{
  ...
}; 

class GliderPlane : public AeroPlaneBase
{
};

All of these planes can do similar things - but they behave slightly differently, so they need a different class to describe their behaviour.

Now, each plane will have zero or more "engines", so the class may have a "has-a" relationship to a PlaneEngine class. The glider, which is an engineless plane doesn't have any engine, the JetPlane can have 8, perhaps...

Likewise, in a roleplaying game, a player "is-a" Character (which is also the baseclass for Monster and the different derived forms of that), say, but "has-a" relationship with the Weapon class. The Character isn't a type of Weapon, but it has a weapon.

Upvotes: 2

thecoshman
thecoshman

Reputation: 8650

Generally speaking, composition is better than inheritance. But it depends on what exactly you want to do. For the most part think:

IS A -> inheritance
HAS A -> composition 

Inherit when you want/need to extend a base class. If you just need to use another class, just have an instance of it with the other class.

Side note, composition and aggregation are basically the same thing. Conceptually slightly different, in code, the same thing.

Upvotes: 3

user2530166
user2530166

Reputation:

It's a matter of design and what you are trying to model. Scott Meyers' Effective C++ will note that public inheritance (the second example) models 'is-a', whereas composition (the first example) models 'is-implemented-in-terms-of' or 'has-a'. So, for your example, you should decide what role KClass is playing and which of these philosophies makes more sense. Just looking at the names KCharacter, KClass, and KPC, it's hard for me to tell their purposes.

Upvotes: 3

Related Questions