The trouble with C++ is that it maintains backwards compatibility with C, so every error-prone thing you could do in C, you can still do in C++, even though C++ may have a better way.
The modern, safest, way to use C++, is to use smart pointers rather than raw pointers, which guarantee that nothing gets deleted until there are no more references to it, and that at that point it will get deleted.
Of course raw pointers and new/delete, even malloc/free, all have their uses, and without these low level facilities you wouldn't be able to create better alternatives like smart pointers, but use these at your own peril, and don't blame the language if you mess up, when you could have just done it the safe way!
The trouble with C++ is that it maintains backward compatibility with C++.
The modern safest way to use C++ involves lifetime annotations and ownership annotations run under multiple built-in Clang constraint solvers, but this isn't what most users do.
> which guarantee that nothing gets deleted until there are no more references to it, and that at that point it will get deleted.
To be more precise, C++'s smart pointers will ensure something is live while specific kinds of references the smart pointer knows about are around, but they won't (and can't) catch all references. For example, std::unique_ptr ensures that no other std::unique_ptr will own its object and std::shared_ptr will not delete its object while there are other std::shared_ptrs around that point to the same object, but neither can track things like `std::span`/`std::string_view`/other kinds of references into their object.