While I was rambling, it seems that others have responded first... But at least I gave a different example of why it's useful -- there are a lot of reasons...
You make data members private to preserve consistency of state. This matters. It matters a lot. You also do it to allow the implementation of a subclass to differ drastically from that of the base class: This is called "abstraction", and it's a big part of the reason why we bother doing OOP at all. So let's take those two in order:
Say for example you've got a class which represents a list of strings. You've got an interface for that class: A count of the number of strings you've got, a number which says how much memory you've got allocated1, and you've got a pointer to the storage: We'll assume for the sake of simplicity that it's an array of pointers to your favorite string class.
Okay. Take for example that number which tracks the size of your array. Assume that some genius comes by and changes it without reallocating (this could be you two weeks later, or even at 3:00 AM the following morning). Okay, you crash. But not always. Bugs like that are a bitch to track down, because you won't crash consistently. Sometimes it'll be a problem, sometimes not; you may get away with using the next few bytes off the end of your array, but when you call realloc() they won't get dragged along with the rest. It's a mess. Don't go there.
The same goes for the data member tracking the number of strings you've got: If it says "five", and you change it to "six", your code will eventually try to call member functions on an object that doesn't exist: p5->size(), for example. You're in trouble again.
In inline function in C++ incurs no overhead, unless it's a virtual (but if you need it to be virtual, you need a function anyway, so you're stuck). Use them.
Of course, there are some public data members which can safely be changed by the "user" -- but which is which? Here's a way to make it easy: If the "user" shouldn't touch it, hide it.
Okay. Now let's assume that you've got code here and there which uses your string list class. You've got other lists of strings which you might like to interact with also, but they can't be stored in that list thing: Say you've got a GUI widget such as a list box. Well, it's got a list of strings, hasn't it? How many sets of functions do you want to write to do the same thing to different classes? My answer is, "one". All lists of strings should have the same interface if at all possible. It's less code to write, less hassle to deal with.
So. Let's say your string list class has an abstract base class (no data members at all, just a pure functional interface, without even bodies for most of the virtual functions (In C++ a bodiless virtual function is called a "pure virtual function") ). If that's the case, you can make one subclass which contains the above-described list of pointers to string objects, and another subclass which provides an interface to the guts of a list box widget. You can interact with both in exactly the same way, using the same code: Just declare a pointer to the abstract base class. Java provides a similar and somewhat simpler feature called "interfaces"2.
The above list/widget example is taken from Borland's VCL library, shipped with their Delphi and C++ Builder products. It's a neat idea. It's the first example that sprang to mind, but there are dozens of others.
In a nutshell: If you don't know why you should use abstraction, you don't know object oriented programming and you don't know C++. This is generally true of all C++ features: If you can't see why on earth anybody would bother, trust me, you're missing something important. This is the same as making the transition to C from an unstructured "goto language" like Basic: Functional decomposition seems kinda silly at first. It really looks like gratuitous rigamarole until you begin to grok. Then grokking happens, and you wonder how you could ever have done it any other way. So it is with OOP.
I recommend learning: It's enormously fun stuff.
1 More than you need, if you've got any sense: If you
allocate more storage than you need, you can add more items to the list without having to
reallocate each time.
2 . . . which you should learn about and think about because there is wisdom implied in what they did with it and in its intended uses.
In
core10k's example, he forgets to
return a
const reference to the
cout object, and he forgets to make the
function an
inline. That's not the way a
C++ programmer would have written it. More to the point, is that
function being called a thousand times in a row, in a tight
loop? If not, you may be sacrificing an awful lot just for a small
optimization on something that only happens once in a while. It's not a very good example. There
are good examples. The point is a valid one:
Abstraction is not always and forever the only way to do things; there is often a cost, and sometimes it costs too much. it's just the best way in most normal cases, for the reasons given above.
For those who are concerned about
C++ having serious
efficiency problems, I recommend
The Design and Evolution of C++ by
Bjarne Stroustrup. It's worth reading if you either use
C++, or have avoided using it.
Furthermore, changing things in the future isn't a "luxury", it's a law of nature in
software development, at least in my experience. Sometimes you have to make sacrifices for the sake of efficiency, but you do it with a
profiler and be sure you're getting something back for your troubles.