C++ is not Java
I started my commercial programming career back in the day with writing test script compilers in C++. That should have helped me a lot when programming Arduinos, but after 20 years of exposure to Java it seems I have forgotten a thing, or two, or ten.
In Java, the VM (virtual machine) hangs on to objects as long as at least one reference exists. If you create a String object in a method and return that object, all is well. If an object is not referenced any more, the garbage collection (GC) takes care of it.
In C++, there is no VM and no GC. Allocating and freeing memory is subject to a few hard rules and yourself. If you create a String object in a method (and don't use malloc()), that object is created on the stack. When you leave the method, anything the method created on the stack is being freed, in including your string object. If you pass a reference to that object to the caller, you pass a reference to middle of nowhere. The compiler will not warn you and the results may be what you intended or not (depending if the memory has been overwritten or not by the time you try to access the object).
- malloc() allocates memory from the heap. On the Arduino, that is not a good idea because allocating and freeing memory creates a swiss cheese of heap memory and you may run out even if you feel you shouldn't.
- The caller creates the object, and your method uses the object it gets handed by the caller. That works well with objects of fixed size (like, "unsigned long") and does not work at all with objects the size of which gets determined by the method called. I usually try to do this for fixed-sized objects.
- The method uses a static buffer. The caller needs to process (or copy) the "returned" value because it will get overwritten by the next call to a method that uses the buffer. I usually try to do this for variable-sized objects like strings.