Most Arduinos (like the Uno or Nano) have very few RAM, thus you first need to make sure, that you never allocate too much memory. Also dynamicallyDynamically allocating memory can also lead to heap fragmentation (heap being the part of memory, where dynamic allocation happens).
In most cases you would want to allocate memory of different sizes (for example arrays of different sizes) or just different objects (with each having it's own size) (!!! This is the key point here). Then you are going to delete some of these objects. That which will create holes inside the memory. They can be filled again with objects with the same or less size. As time passes and more allocation and deleting happens, these holes tend to get smaller, up to the point, where none of your new to allocate objects can fit in there. That memory then is unusable. This phenomenon is called heap fragmentation.
These holes appear naturally, also on a PC. But but there are 2 key differences:
The Arduino has such little RAM, that the holes can fill up your memory very very fast.
While the PC has an operating system, which manages the RAM (defragmenting it or putting unused stuff away into a paging/swap file), the Arduino does not have an OS. So noone keeps an eye on the real available RAM and noone tidies up the memory once in a while.
That does not mean, that you cannot use dynamic allocation on an Arduino, but that is very risky depending on what exactly you are doing and how long the program should work without failing.
Considering this big caveat, you are very limited on how to use dynamic allocation. Doing it too much will result in very unstable code. The remaining possibilities, where it might be safe to use it, can also be easily done with static allocation. For example take your queue, which is basically a linked list. Where is the problem with allocating an array of QueueItems at the start. Each item gets a way to determine, if it is valid. When creating a new item, you just pick the first element in the array, which has a non-valid item, and set it to the desired value. You still can use the data via the pointers, just as before. But, but now you have it with static allocation.
You might find, that the code looks uglier that way, but you need to adapt to the platform, that you use.
Note, that this does not apply, when you are going to create only objects with the same size. Then anyAny deleted object will leave a hole, where any new object can fit into. The compiler uses that fact. So in that case you are safe. Just every object, that you dynamically create in your program, needs to be the exact same size. That of course also includes objects, that are created inside different libraries or classes. (ForFor this reason it can still be a bad design choice, as you or others (if you want to publish your code), may want to pair your library with other code).
Another way to be safe is to only create and delete objects in closed cycles, meaning, that a created object needs to be deleted, before the next object is created. Though that is not fitting for your application.
On bigger'Bigger' microcontrollers, for example the non-Arduino boards with the ESP32, have much more memory. Thus the use of dynamic allocation is not that bad on them. Though you still don't have an OS to manage the RAM.