Yes, they can handle the hooks independently.  The problem is that they're getting irrelevant callbacks.  Let's say you have VoxelSystem A with Node A, and VoxelSystem B with Node B, both nodes having a known buffer index of zero.  When you delete Node B, both VoxelSystem A and VoxelSystem B are notified, and they both add zero to their free indexes lists.  That is correct for VoxelSystem B, but incorrect for VoxelSystem A.  See the problem?

When I mentioned virtual functions, I just meant as a means of simplifying the process of supplying and managing callbacks.  Rather than having a function pointer and an opaque void* for instance data (and an integer hook id), just use an interface with a virtual function:

class DeleteHook {
public:
     virtual void nodeDeleted(VoxelNode* node) = 0;
};

Have VoxelSystem, etc., implement that interface, then pass a DeleteHook* to addDeleteHook and removeDeleteHook.  No need for IDs: just store the pointers in a vector (or hash set, if you're worried about performance, though there's no need to).