Today, I was thinking about the uses of void* pointers and if there was any occasion where it would look better in C++. I couldn't think of any and I thought I'd explain why.
In C, the use of void pointers can be extremely useful. It can be used as a universally generic pointer that can point to any type. The limitation to this is that you must know the type that the void pointer was casted from in order to obtain data from it (which usually isn't that bad). Here's example using a simple list struct.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
//Credit towards GLib I guess since they're the ones who helped me understand the concept of the void pointer years ago.
#include <stdlib.h>
typedef struct _LList
{
void *data;
struct _LList * next;
} LList;
//Returns new beginning to the list.
LList* list_prepend(LList* _node, void *_data)
{
//NOTE! This is a memory leak! Be sure to free when in practice!
LList * tmp = (LList*)malloc(sizeof(LList));
tmp->data = _data;
tmp->next = _node;
return tmp;
}
int main()
{
LList *myList = NULL;
int someType = 324;
myList = list_prepend(myList, (void*)&someType);
//Technically, the list can hold any data type.
//Because of this, the container lacks consistency and can be more confusing.
}
| |
This shows that a struct that resembles a simple list node. Notice that in the struct, we have a dataype of void* that holds our data.
One may ask how we hold data if we don't know the data type? There are two answers actually. In actuality, you'll know the data type by the time you create the list so you can implement a list specific to your datatype, especially if they're non-primitive. You can imagine how annoying this would be. Or, my favorite C way, is to use void* pointers that can hold any pointer type! This gives a more dynamic view on basic C containers which you'll find a lot in glib and a few other general C libraries.
Another may ask how we handle the data if we don't know what data type it is? There is only one answer: You don't. The purpose of the list above is to handle pointers to data, not data themselves. There are a few rare occasions where handling data using void pointers through abstract functions is considered good but I honestly don't know about them. I'm sure GTK+ does it a few times. NOTE: TODO: FIXME: This isn't exactly what I'm wanting to say and is factually not right. In Win32, they use "window handles" (HWND) which happen to be void pointers to keep the contents of the window struct away from the Win32 developer. Although I'm ready to say you shouldn't do this ever, I don't have the experience to say that.
Now, note that this seems rather lumpy. There is no datatype consistency in the list e.g. you can have an integer that links to a struct, etc. Also, as I am a fan of beautifying code as much as possible, you must cast the pointer you wish to give to any of the list functions to void* first.
Here at C++, we don't settle for lumpy (actually, we do more often than one would know...) or code uglification (fancy word). If we know the data type at the time of List use, why can't we just pass the datatype itself to the list or use a macro or something? Well in C, we can use macros but even this isn't recommended really and can be rather messy. In C++, we have this awesome thing called templates which are heavily underused and often overused. So, this ugly void pointer struct thingy can be turned into:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
template<typename X>
struct LList
{
private:
struct Link
{
Link(X _data, Link * _next) : data(_data), next(_next) {}
X data;
Link *next;
};
Link *_first;
public:
LList() : _first(NULL) {}
~LList() {} //OMG LEAK! Make sure to delete the variables when in practice!
void Prepend(X _data)
{
Link *tmp = _first;
Link *newLink = new Link(_data, tmp); //This is a potential leak...
//A safer approach would be to use smart pointers in case the list is broken.
_first = newLink;
}
};
int main()
{
LList<int> tmpList;
tmpList.Prepend(32432); tmpList.Prepend(3243);
//The list may only contain types of int!
}
| |
Read below if you want, was more of me just rambling...
To be fair, they are not the same thing however void pointers and templates can somewhat relate to each other. void pointers are used in C where templates are often used in C++ almost to a 1 to 1 ratio (if you were to count c function pointers as a void pointer WHICH IT IS NOT BUT JUST SAYING. In C, they use callbacks, where in C++, we can use class templates. Would anyone care to disagree?
NOTE: Doesn't really feel like an article but whatever...