Using Void Pointers and Generics with the C++ Programming Language
C++ Programming Language is currently a widely used powerful language, which allows for Object Oriented Development with Classes, and also has advanced features such as the Standard Template Library (STL) and also the use of Generics. Not only is it fully Object Oriented, but C++ also allows for low level memory management, and the C Programming Language is backwardly compatible with C++.
C++ is particularly well suited for large, complex systems development. Currently, it is the main software platform for the F-35 Joint Strike Fighter, which is the worlds most technologically advanced aircraft. C++ is also the primary software for launch systems of the NASA SLS Rocket, as well as the launch systems for Missile Defense Agency Rockets.
The NASA Artemis SLS Rocket is the new rocket for moon missions and beyond, The launch systems software for the SLS rocket is primarily C++.
The F-35 Joint Strike Fighter, also known officially as the Lightning II, is manufactured by Lockheed Martin in a joint partnership with the USA Department of Defense and various USA Allies, such as the United Kingdom, Australia, Canada, Norway, and others. It is the most technologically advanced aircraft ever made. Its software is primarily C++.
One thing I encountered in past software work I did, was the use of VOID Pointers (VOID*). At first I was a little confused by them, and did some research, void pointers are primarily a thing that was used with the C programming language, and were used when the developer was unsure of what the type for a particular variable would be during his development. Also, memory allocation in C with the malloc command always returns a void pointer, which C programmers normally cast to another type right after the method is called. Void pointers have the following properties.
- Do not have a type, are typeless, but can hold the memory address of another variable
- A void pointer can point to objects of any type
- Cannot be dereferenced, since the computer has no idea of the amount of memory taken up
- Can only be dereferenced after being cast as another variable type
- Cannot have pointer arithmetic done on them
- Although some compilers allow deleting a void pointer that points to dynamically allocated memory, doing so should be avoided, as it can result in undefined behavior.
As part of the Department of Defense Joint Strike Fighter Project, a document was released to the public domain on standards for safety-critical C++ software, the F-35 Joint Strike Fighter C++ Coding Standards Document.
F-35 Joint Strike Fighter C++ Coding Standards Document CLICK HERE
According to this document, void pointers and cast of variables to other types SHALL not be used in safety-critical C++ software. In this blog post I will show us how to work with and manage void pointers, and also detail how C++ has replaced void pointers with generic programming with templates. I will show example code of working with both void pointers and also generic type templates.
First we will start with our include files for void pointers and template generics for our example C++ program.
#include <iostream> #include <string> #include <vector> #include <cstdlib> using namespace std;
Next, we will declare some integer, float, and char string variables, and also our void pointer, to use in our example.
// declare vars int nNumber = 10; float fNumber = 10.01; char cCharArray[] = "This is an example C++ application."; // void pointer void* ptrVoid;
Also, not a part of our example program for this blog post, here is an example of how a void pointer is returned by memory allocation using MALLOC command in C programming language, and then immediately cast to another type.
struct zooAnimal *zooKeeper = NULL; // create a 3 year old 300 pound male lion, make start node of list struct zooAnimal *lion1 = (struct zooAnimal*)malloc(sizeof(struct zooAnimal)); if (NULL == lion1) { printf("\n Lion creation failed \n"); return 1; } lion1->type = Lion; lion1->sex = Male; lion1->age = 3; lion1->weight = 300; lion1->next = NULL; // the zoo keeper starts with this lion // before going through the list of animals zooKeeper = lion1;
Then in our next section of code for our example program, we will demonstrate setting the void pointer variable to the memory address of another variable, and then show to cast it to another variable type with two different methods, using the C++ STATIC CAST method, and also doing a C-style cast as well. Take note, it is far safer and much better software development to use a C++ static cast instead of a C-style cast.
// set void pointer to address of integer number, // then cast to integer so it can be dereferenced ptrVoid = &nNumber; cout << endl << "Void pointer to integer address: " << ptrVoid << endl << "dereferenced value static cast: " << *static_cast(ptrVoid) << endl << "dereferenced value after C-style cast: " << *((int*)ptrVoid) << endl << endl; // set void pointer to address of float number, // then cast to float so it can be dereferenced ptrVoid = &fNumber; cout << "Void pointer to float address: " << ptrVoid << endl << "dereferenced value static cast: " << *static_cast (ptrVoid) << endl << "dereferenced value after C-style cast: " << *((float*)ptrVoid) << endl << endl; // set void pointer to address of char array string // then cast to dereference // note with char arrays we do not dereference the pointer // but the non-pointer name of the array ptrVoid = &cCharArray; cout << "Void pointer to char array address: " << ptrVoid << endl << "dereferenced value static cast: " << static_cast (ptrVoid) << endl << "dereferenced value after C-style cast: " << (char*)ptrVoid << endl << endl;
This is what our output will look like after running the example application for void pointers:
Note how the memory addresses for each variable are sequential and occupy the same area in computer memory, this is a good sign that the C++ compiler is compiling machine code that efficiently uses computer memory.
These are good examples of how you would use void pointers in C++ development. However, in C++ there is feature that provides the same functionality as void pointers, but is better to use and allows for object oriented development, which C does not allow. This feature is called generic programming and allows the use of generic class templates and generic function templates, which allow for type-independent development. In our next code examples, we will show to do C++ generic programming.
First, we will define a template function in our C++ code, outside of the main() method, like the following:
// we use a function template for generic programming templateT const& Maximum(T const& a, T const& b) { if (a > b) return a; if (b > a) return b; }
Note that there is not a specific type for the Maximum() function parameter, but instead a generic variable called T that can be used for any type.
Next, we will declare variables of different type and call the Maximum() generic function template with them to determine which one is the largest. Note we are using the same function with different types of variables. This is the essence of generic programming in C++, and is a powerful feature for code reusability.
// after the advent of generics in C++ // void pointers, a holdover from C programming, // were no longer needed // vars to use with function template int x = 11; int y = 13; float i = 10.56; float j = 14.32; string k1 = "C++"; string k2 = "Application"; // output the results cout << "Maximum integer x or y: " << Maximum(x, y) << endl << endl; cout << "Maximum float i or j: " << Maximum(i, j) << endl << endl; cout << "Maximum string k1 or k2: " << Maximum(k1, k2) << endl << endl;
This is what our output will look like after running our example C++ program.
Next, we will define a class template for a class to be used with generic programming in C++. Enter the following code outside of our main method to define our class template. Note we are reusing the generic T type, and not a specific type of variable. Note that in the outputList() method, we are using a vector iterator to loop through the elements, in this case the TYPENAME keyword must be used since it is a generic iterator.
// class template for generic programming templateclass ElementList { private: vector elements; public: void addElement(T const&); T getLastElement() const; void outputList(); }; // addElement method template void ElementList ::addElement(T const& element) { elements.push_back(element); } // getLastElement method template T ElementList ::getLastElement() const { // return last element return elements.back(); } // output ElementList elements template void ElementList ::outputList() { cout << "ElementList elements: "; for (typename vector ::iterator it = elements.begin(); it < elements.end(); it++) { cout << (*it) << " "; } cout << endl << endl; }
Then we will place the following code in our main() method to instantiate objects of type ElementList, and also to output the results of using our generic class template. Note once again we are using only one class for different types of variables, called generic programming.
// vars to use with class template ElementListstrList; ElementList fList; ElementList nList; // add string values to ElementList string object strList.addElement("C++"); strList.addElement("Object Oriented"); strList.addElement("Application"); // add float value to ElementList float object fList.addElement(3.14); fList.addElement(13.1); fList.addElement(26.2); // add integer values to ElementList int object nList.addElement(3); nList.addElement(11); nList.addElement(27); // get the most recent string added to list cout << "String List last element: " << strList.getLastElement() << endl << endl; // get the most recent float element cout << "Float List last element: " << fList.getLastElement() << endl << endl; // get the most recent integer element cout << "Integer List last element: " << nList.getLastElement() << endl << endl; // output the string list strList.outputList(); // output the integer list nList.outputList(); // output the float list fList.outputList();
This is what our output will be after running our example C++ program with generic class template.
Here is a copy-and-pastable listing of our entire C++ example application.
// CPPTemplatesPointers.cpp // // By Michael G. Workman // // This C++ console application displays // usage of VOID pointers and generic templates // // This example application is freely distributable // under terms of MIT open source license // https://opensource.org/licenses/MIT #include <iostream> #include <string> #include <vector> #include <cstdlib> using namespace std; // class template for generic programming template <class T>class ElementList { private: vector<T> elements; public: void addElement(T const&); T getLastElement() const; void outputList(); }; // addElement method template <class T> void ElementList<T> ::addElement(T const& element) { elements.push_back(element); } // getLastElement method template <class T> T ElementList<T> ::getLastElement() const { // return last element return elements.back(); } // output ElementList elements template <class T> void ElementList<T> ::outputList() { cout << "ElementList elements: "; for (typename vector<T> ::iterator it = elements.begin(); it < elements.end(); it++) { cout << (*it) << " "; } cout << endl << endl; } // we use a function template for generic programming template <typename T> T const& Maximum(T const& a, T const& b) { if (a > b) return a; if (b > a) return b; } int main() { // declare vars int nNumber = 10; float fNumber = 10.01; char cCharArray[] = "This is an example C++ application."; // void pointer void* ptrVoid; // change void pointer to address of integer number, // then cast to integer so it can be dereferenced ptrVoid = &nNumber; cout << endl << "Void pointer to integer address: " << ptrVoid << endl; cout << "dereferenced value static cast: " << *static_cast<int> (ptrVoid) << endl; cout << "dereferenced value C-style cast: " << *((int*)ptrVoid) << endl; // set void pointer to address of float number, // then cast to float so it can be dereferenced ptrVoid = &fNumber; cout << "Void pointer to float address: " << ptrVoid << endl; cout << "dereferenced value static cast: " << *static_cast<float> (ptrVoid) << endl; cout << "dereferenced value C-style cast: " << *((float*)ptrVoid) << endl; // set void pointer to address of char array string, // then cast to dereference // note with char arrays we do not dereference the pointer, // but the non-pointer name of the array ptrVoid = &cCharArray; cout << "Void pointer to char array address: " << ptrVoid << endl; cout << "dereferenced value static cast: " << static_cast<char> (ptrVoid) << endl; cout << "dereferenced value C-style cast: " << (char*)ptrVoid << endl; // after the advent of generics in C++ // void pointers, a holdover from C programming, // were no longer needed // vars to use with function template int x = 11; int y = 13; float i = 10.56; float j = 14.32; string k1 = "C++"; string k2 = "Application"; // output the results cout << "Maximum integer x or y: " << Maximum(x, y) << endl << endl; cout << "Maximum float i or j: " << Maximum(i, j) << endl << endl; cout << "Maximum string k1 or k2: " << Maximum(k1, k2) << endl << endl; // vars to use with class template ElementList<string> strList; ElementList<float> fList; ElementList<int> nList; // add string values to ElementList string object strList.addElement("C++"); strList.addElement("Object Oriented"); strList.addElement("Application"); // add float values to ElementList float object fList.addElement(3.14); fList.addElement(13.1); fList.addElement(26.2); // add integer values to ElementList int object nList.addElement(3); nList.addElement(11); nList.addElement(27); // get the most recent string element cout << "String List last element: " << strList.getLastElement() << endl << endl; // get the most recent float element cout << "Float List last element: " << fList.getLastElement() << endl << endl; // get the most recent integer element cout << "Integer List last element: " << nList.getLastElement() << endl << endl; // output the string list strList.outputList(); // output the integer list nList.outputList(); // output the float list fList.outputList(); return 0; }
This example C++ program was created in Microsoft Visual Studio IDE running on Windows 10. Microsoft Visual Studio Community Edition is a free download, while the Professional and Enterprise versions can be purchased. This example C++ program will run equally well on Unix and Linux with either the g++ compiler or the CLANG compiler.
For those who wish to try out and learn Unix, there is a NetBSD Unix shell account available from SDF at http://sdf.org for $9 every 3 months. Also, a free OpenBSD Unix shell account is available on https://tilde.institute. There is free Linux offerings on the internet as well. A free Ubuntu Linux shell account is available at https://tilde.team. Many Linux versions are available as free downloads on the internet as well.
I hope these examples have been very helpful for you to understand void pointers and generic programming in C++. Generic programming is a powerful feature of the C++ programming language that allows for code reusability and simplicity.
To see my Curriculum Vitae, go to
To see this project in Azure DevOps version control, go to C++ Void Pointers and Generics
To see all my projects on Azure DevOps, go to https://dev.azure.com/AbionTechnology/
To see my Posts and Answers on Stack Overflow,
go to Michael G. Workman on Stack Overflow
If you have any questions about C, C++, Microsoft C# .NET, Microsoft Azure Cloud, Unix, Linux, and/or Mobile Apps, please feel free to contact me by email at:
michael.g.workman@outlook.com