Void pointers

The void pointer (also called a generic pointer) is a special pointer type that can point to objects of any data type. Declare a void pointer using the void keyword as the pointer's type:

void* anyPtr{};

A void pointer can point to any data type:

int health{};
double attackPower{};

struct Character
{
    int level;
    double experience;
};

Character player{};

void* anyPtr{};
anyPtr = &health;
anyPtr = &attackPower;
anyPtr = &player;

However, because void pointers don't know what type they point to, dereferencing them directly is illegal. You must first cast the void pointer to the appropriate type:

int gold{ 500 };
void* vPtr{ &gold };

int* intPtr{ static_cast<int*>(vPtr) };

std::cout << *intPtr << '\n';

Output:

500

The obvious question: if a void pointer doesn't know what it points to, how do you know what to cast it to? That's your responsibility to track.

Here's a practical example:

#include <cassert>
#include <iostream>

enum class StatType
{
    HEALTH,
    DAMAGE,
    NAME
};

void displayStat(void* ptr, StatType type)
{
    switch (type)
    {
    case StatType::HEALTH:
        std::cout << *static_cast<int*>(ptr) << '\n';
        break;
    case StatType::DAMAGE:
        std::cout << *static_cast<double*>(ptr) << '\n';
        break;
    case StatType::NAME:
        std::cout << static_cast<char*>(ptr) << '\n';
        break;
    default:
        std::cerr << "displayStat(): unknown type\n";
        assert(false && "type not handled");
        break;
    }
}

int main()
{
    int playerHealth{ 100 };
    double weaponDamage{ 45.5 };
    char heroName[]{ "Alaric" };

    displayStat(&playerHealth, StatType::HEALTH);
    displayStat(&weaponDamage, StatType::DAMAGE);
    displayStat(heroName, StatType::NAME);

    return 0;
}

Output:

100 45.5 Alaric

Void pointer miscellany

Void pointers can be null:

void* ptr{ nullptr };

Deleting a void pointer causes undefined behavior because the pointer doesn't know what type (and therefore what destructor) to use. If you must delete through a void pointer, first cast it back to the correct type.

Pointer arithmetic doesn't work with void pointers. Arithmetic requires knowing the object size to calculate offsets, and void pointers don't have that information.

Void references don't exist. A void& wouldn't know what type it references, making it meaningless.

Conclusion

Avoid void pointers when possible. They bypass type checking, allowing nonsensical operations that compile without warnings:

    int playerHealth{ 100 };
    displayStat(&playerHealth, StatType::NAME);

This compiles but produces garbage output or undefined behavior.

The displayStat() function above seems clever for handling multiple types, but C++ offers better alternatives. Function overloading handles multiple types while preserving type safety. Templates provide even more flexibility with full type checking.

If you encounter a situation where void pointers seem necessary, first verify that function overloading, templates, or std::variant won't solve the problem more safely. Void pointers should be a last resort.

Summary

Void pointers: Declared as void*, can point to objects of any type. They trade type safety for flexibility.

Cannot dereference directly: Void pointers don't know their pointed-to type, so dereferencing is illegal. Cast to the appropriate type using static_cast first.

Tracking type information: Programmers must track what type a void pointer references, typically using an accompanying enum or other mechanism.

Void pointer limitations: Can hold nullptr, but deleting causes undefined behavior (cast first). Pointer arithmetic doesn't work. No such thing as a void reference.

Type safety concerns: Void pointers bypass type checking, allowing operations that compile but don't make sense. Errors only appear at runtime.

Better alternatives: Function overloading and templates handle multiple types with full type checking. std::variant provides type-safe unions. Prefer these over void pointers.

Void pointers sacrifice type safety for flexibility. Use them only when no safer alternative exists.