Summing up the problem: You want (1) flexibility enough to compose the behaviour of your entities, while not incurring (2) run-time overhead or code maintenance problems. Using inheritance or multiple inheritance will be efficient runtime-wise but scale poorly. Using a component architecture will in a large-scale application with a rich set of components and densly combined entities scale nicely but be increasingly expensive, eventually prohibitively so.
In a sophisticated C++ design this is a non-issue. If you accept defining your behaviour in code (as opposed to external scripts) you can have the full flexibility of components with no runtime overhead if you use policy-based templates, as per A. Alexandrescu (2009). As with all templates this may increase compilation times.
So we want flexibility about movement, attacking and interactions, or whatnot. Say we define an entity as
template<typename MOVE, typename ATT, typename INTER>
class Entity : public MOVE, public ATT, public INTER
{
...
};
You can specify any number of policy classes and combine these to your hearts' content:
struct SessilePolicy
{
void move(double deltat) {}
};
struct MovePolicy
{
void move(double deltat) { RunSomewhere(); }
};
struct PacifistPolicy
{
void attack() {}
};
struct PsychoPolicy
{
void attack() { GoBeserk(); }
};
struct ShootLaserFromEyesPolicy
{
void attack() { EatThis(); }
};
struct NoninteractablePolicy
{
void interact() {}
};
struct ChattyPolicy
{
void interact() { Eliza(); }
};
Now you can compose your game entities
typedef Entity<SessilePolicy, PacifistPolicy, NoninteractablePolicy> DummyEntity;
typedef Entity<MovePolicy, PsychoPolicy, NonteractablePolicy> GenericEnemy;
typedef Enity<MovePolicy, ShootLaserFromEyes, ChattyPolicy> ExampleFromPreviousPost;
ExampleFromPreviousPost myWeirdHouse;
In short, we live in a richer world of architecture than just having to choose between inheritance or components. Not many people code in Smalltalk nowadays, but this language separates inheritance of types from inheritance of implementation, and just as Objective-C uses signalling to communicate. Less frequently used languages, but a fourth architectural option.
Template composition of policy classes is one way to achieve the same results as components but without sacrificing runtime efficiency or code manageability, while retaining the full type-safety and debugging tools of C++, which you wouldn't have in scripting.