A handle is an identifier to an abstract objects (bitmaps, sockets, files, memory, etc...) used to identify which object is affected by an operation: HBITMAP
, socket
, FILE*
are example of handles -- they are in fact integers possibly representing an index in some table or map in the operating system.
Strictly speaking, a pointer is a precise location in memory. While a pointer is sometimes used as a handle, the inverse is rarely (if ever) true.
In object oriented environments, handles are considered low level entities and it is a good practice to wrap handles in objects with the object having methods that calls the operations using the allocated handle. In that case, you have a reference to an object holding an handle, for instance: CBitmap
, System.Net.Sockets.Socket
, std::fstream
, etc.
Without delving into a language religious war, some will argue that one or the other is cleaner, safer, faster, easier. It almost always depend on what environment you are in -- you would be ill advised to use handles directly in C# if you have a choice, while in C it will be much simpler (and necessary) to use handles.
Important Note: In the .Net environement, if you ever have to make marshaling, you will end up reading something about the actual objects references being called handles. This is because they are in fact handles under the hood rather than pointers. So when you call a method on an object, the compiler is in reality calling the method with an handle to an object that can freely move in memory. This makes it possible for the garbage collector to avoid memory fragmentation. So with a System.Drawing.Bitmap
you end up with a Handle to a Pointer to a Handle.
EDIT:
Example, stdio/fstream in C++:
// Read a short line from a file and output it to the console
// 256 bytes max for simplicity purposes
char buffer[256];
// With handle
FILE* file = fopen ("file.txt" , "r");
fgets(buffer, 256 , file);
std::cout << buffer << std::endl;
fclose (file);
// With object reference
{
std::ifstream stream ("file.txt");
stream.getline(buffer, 256);
std::cout << buffer << std::endl;
}
The top example uses what should be considered a handle FILE*
to a file. The file
handle is allocated using fopen
and passed to operations such as fgets()
and close()
. close()
deallocate the handle.
The bottom example uses std::ifstream
. The handle is allocated in the object constructor and is internal to that object. To make operations on the file, you use the method provided by that object such as getline()
. The handle is deallocated by the stream destructor when the object goes out of scope, that is, at the closing bracket, or if the object was allocated on the heap, you would need to delete it explicitely.