C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 8:
[8.8] What is a handle to an object? Is it a pointer? Is it a reference? Is it a pointer-to-a-pointer? What is it?

The term handle is used to mean any technique that lets you get to another object — a generalized pseudo-pointer. The term is (intentionally) ambiguous and vague.

Ambiguity is actually an asset in certain cases. For example, during early design you might not be ready to commit to a specific representation for the handles. You might not be sure whether you'll want simple pointers vs. references vs. pointers-to-pointers vs. references-to-pointers vs. integer indices into an array vs. strings (or other key) that can be looked up in a hash-table (or other data structure) vs. database keys vs. some other technique. If you merely know that you'll need some sort of thingy that will uniquely identify and get to an object, you call the thingy a Handle.

So if your ultimate goal is to enable a glop of code to uniquely identify/look-up a specific object of some class Fred, you need to pass a Fred handle into that glop of code. The handle might be a string that can be used as a key in some well-known lookup table (e.g., a key in a std::map<std::string,Fred> or a std::map<std::string,Fred*>), or it might be an integer that would be an index into some well-known array (e.g., Fred* array = new Fred[maxNumFreds]), or it might be a simple Fred*, or it might be something else.

Novices often think in terms of pointers, but in reality there are downside risks to using raw pointers. E.g., what if the Fred object needs to move? How do we know when it's safe to delete the Fred objects? What if the Fred object needs to (temporarily) get serialized on disk? etc., etc. Most of the time we add more layers of indirection to manage situations like these. For example, the handles might be Fred**, where the pointed-to Fred* pointers are guaranteed to never move but when the Fred objects need to move, you just update the pointed-to Fred* pointers. Or you make the handle an integer then have the Fred objects (or pointers to the Fred objects) looked up in a table/array/whatever. Or whatever.

The point is that we use the word Handle when we don't yet know the details of what we're going to do.

Another time we use the word Handle is when we want to be vague about what we've already done (sometimes the term magic cookie is used for this as well, as in, "The software passes around a magic cookie that is used to uniquely identify and locate the appropriate Fred object"). The reason we (sometimes) want to be vague about what we've already done is to minimize the ripple effect if/when the specific details/representation of the handle change. E.g., if/when someone changes the handle from a string that is used in a lookup table to an integer that is looked up in an array, we don't want to go and update a zillion lines of code.

To further ease maintenance if/when the details/representation of a handle changes (or to generally make the code easier to read/write), we often encapsulate the handle in a class. This class often overloads operators operator-> and operator* (since the handle acts like a pointer, it might as well look like a pointer).