I should point out, however, that many OO languages (like Java and C++) make inheritance much easier than composition, and as such inheritance seems more useful than it actually is, simply because it's all you've got. In languages that have better syntactic support for delegation and other compositional techniques, the usefulness of inheritance is significantly lessened. In other words, the usefulness of inheritance is more accidental (i.e. because you often don't have other good options) rather than inherent (i.e. because there simply aren't better options that are possible or available in other languages).
this makes for a double whammy in terms of extensibility, since not only is the code literally more conducive to later changes, but it meets developer's expectations, too.
encapsulation and inheritance go hand-in-hand in my book, though i suppose encapsulation, and the necessary concept of interface contracts, is an axiom of sorts from which many other good practices can result, especially if there is also a culture of "communicating for extensibility by new developers later."
The second problem is that re-use is an easy problem to solve in any language. We have delegation, we have proxies, we have so many tools. Why use inheritance, which mixes two different concepts (is-a and was-a a/k/a is-implemented-using)?
Lastly, how is the code more conducive to later changes? In my experience, you now have a problem where changing class A changes the behaviour of every class that extends A. This is a notoriously tricky problem in the real world. In theory, that change ought to change every subclass and inheritance is a win. In practice, the people writing all those subclasses did so making assumptions about the behaviour of A at the time it was written, much as there are billions of lines of windows code that depend on its bugs and break the moment you fix a bug.
Encapsulation is a wonderful thing. I do not see implementation inheritance as having anything whatsoever to do with encapsulation. If anything, I see the opposite.
If I write that B is-a A, if these things are fully encapsulated, shouldn't it be a black box as to how B manages to behave exactly like an A? Maybe there's some shared code, but then again, shouldn't it be possible for B to use some other completely different implementation? That's encapsulation to me. Forcing interface inheritance to be twinned with implementation inheritance breaks encapsulation in my opinion.
I also second your point on code reuse. Code reuse/removal of duplication is the main reason behind all programming languages and paradigms. Inheritance is a tool provided by OOP languages. Therefore, to say that it enables code reuse is basically trivial.
As an example, let's assume we're developing a game where cats battle all sorts of other animals for survival. Certainly, the designer has good reason to call the object representing the protagonists in the game Cat. The programmer also has good reasons to call the enemies of the cat Animals, since they will all do the same thing (e.g. attack the cat). However, in this case a Cat is definitely not an Animal. Cat is an overloaded word - it no longer means "feline". Similarly, Animal means something completely different - it's more akin to "enemy". If we could substitute the "natural" ontology of these words with the one our objects define, we would be able to tell that the statement "A Cat is an Animal" is senseless.
I think OOP actually tries to take advantage of the "natural" ontologies we have constructed through experience. This is why this is such a subtle design issue. I'm not quite ready to say it "gets it backwards", but it certainly doesn't mirror the way we naturally look at the world.
I hope I'm mirroring and expanding on what's been said here. I'd love to read your thoughts.
Good design is hard.
Edit: Also, what raganwald says.
(if you meant pattern matching as in text/regular expressions, disregard what I just wrote)
Also, you can certainly have encapsulation even in pure C with proper use of header files and use of typedefs, though C in no way requires you to encapsulate anything. Ever wondered what is the FILE in FILE* that many C/Posix functions use? I certainly have. But it's not necessary to expose it to callers of functions that use it. So much for encapsulation and inheritance going hand in hand.
Composition can work for a lot of these problems too. It depends on the scale of the modification, I guess.
You can start at http://c2.com/cgi/wiki?ArgumentsAgainstOop , and follow the links out of "Inheritance" for at least a decent start at the topic if you've never heard of this criticism.