One Class to rule them all?

You could indeed use a std::vector to eliminate the need for a maximum capacity, but dynamically growing a vector is not ideal on a microcontroller.

I'd suggest the following:

  1. Create a struct DoublyLinkable with a next and prev pointer.
  2. Create a DoublyLinkedList class to maintain a linked list of such DoublyLinkable nodes, with operations to insert or remove nodes from the list.
  3. Make your class inherit from DoublyLinkable.
  4. Add a static DoublyLinkedList to your class.
  5. Add each instance to the list in the constructor of your class, remove it from the list in the destructor.

This approach requires no dynamically allocated storage, just one or two extra pointers in each instance. If you don't have specific requirements on the order, you can use a singly linked list.

You can find an implementation here:

Example:

#include <Arduino_Helpers.h>
#include <AH/Containers/Updatable.hpp> // UpdatableCRTP
#include <AH/STL/memory>               // make_unique

struct DummyClass : UpdatableCRTP<DummyClass> {
  const char *name;
  DummyClass(const char *name) : name(name) {}
  // Some function that does something
  void print(const char *msg) { Serial << name << " says " << msg << endl; }
  // Call the “print” function for all active instances
  static void printAll(const char *msg) {
    applyToAll(&DummyClass::print, msg);
    Serial.println();
  }
  // Get the list of all active instances
  static DoublyLinkedList<DummyClass> &instances() { return updatables;  }
};

void setup() {
  Serial.begin(115200);
  while (!Serial);

  // Create two instances
  auto a = AH::make_unique<DummyClass>("A");
  auto b = AH::make_unique<DummyClass>("B");
  DummyClass::printAll("message 1");
  
  // Disable the first instance
  a->disable();
  DummyClass::printAll("message 2");
  
  // Enable the first instance
  a->enable();
  DummyClass::printAll("message 3");
  
  // Delete the first instance
  a.reset();
  DummyClass::printAll("message 4");
  
  { // Create a scoped local instance
    DummyClass c = "C";
    DummyClass::printAll("message 5");
  } // Local instance is deleted
  DummyClass::printAll("message 6");

  // Another instance
  DummyClass d = "D";

  // Iterate over all instances
  Serial.print("Active instances: [ ");
  for (DummyClass &inst : DummyClass::instances()) {
    Serial << inst.name << ", ";
  }
  Serial.println("]");
}

void loop() {}

Output:

A says message 1
B says message 1

B says message 2

B says message 3
A says message 3

B says message 4

B says message 5
C says message 5

B says message 6

Active instances: [ B, D, ]

That would be exactly the same as a public: static member function in C++. But like J-M-L said, it's often better to use free functions. Not everything has to be a class.

What makes you think that?

instance is not a pointer, it cannot be null. Either way, never use NULL, use nullptr.

This is a memory leak.

Unlike in Java, you almost never use new in C++. If you need dynamic allocations, use std::make_unique. If you just want to create an object, allocate it on the stack (which is the default).

If you want to correct your code, you can find many C++ singleton classes online, but I don't think it will solve your problem.