C++ is a fantastic language. It may be a bit too fantastic, offering too many solutions to the same problem, and too many ways to shoot yourself in the foot. It is known for its awkward constructs that have been "forbidden" for not-so-good reasons, and the famous paper "goto considered harmful" shows it, even if it's targetted at another language.
Of course the real solution is to switch to Rust, but that may not be always possible nor desirable, you are responsible for your choice of language. If you need to stick to C++, read what's below.
Now it's my turn to add a new rant on the language: DO NOT USE new IN YOUR CODE. There is simply no valid reason to use it anymore. It has been a shortcut, and an immense source of memory leaks over the years. No, really, try something else.
Of course it's just a ridiculous rant. Don't make this a hard rule nor take this too seriously. Also, "placement new" is a very powerful construct that should not be forgotten. This paper is just a provocative way of presenting alternatives to the 'new' keyword and warn about its caveats.
... because modern C++ containers are enough
Modern C++ has its weaknesses, but it drastically improved its set of containers. Just put containers in your classes instead of putting pointers all over the place. Modern C++ allows to pass constructor parameters to the default elements of containers, and that is a game changer.
Also, consider the overhead of static arrays. Allocating a string ? Maybe a fixed-size buffer of 64k on the stack will do it, because it's much faster to allocate than calling 'new' and creating a very small buffer with a lot of overhead. On modern machines with multi-gigabytes of RAM, allocating an oversize buffer of 64k to store an intermediate buffer to compute a file name (with the correct buffer overflow detection, you still have to do correct code) may be simpler, more efficient.
... because your application state will be all over the place
There is one special kind of data that is difficult to handle without using 'new': that's the application state. Variables that are highly shared between many parts of your application and that travel a lot. Just creating an interface class with a well-designed set of virtual methods, allocating it with 'new' and passing the pointer around is a very popular way of doing things. However, sooner or later, you will want to have a centralized access to all these variables, or else it will become impossible to manage. You will have pointers, then it won't be enough, so you will have smart pointers, then you will have cyclic references, so you will need a garbage collector, and you will say "I knew I needed Java or C#".
There is an elegant way to solve this: declare a class that will store all this data in a centralized fashion. This class will represent the state of your application. Everything that needs to live beyond local variables needs to be referenced, directly or indirectly, by this class. You won't need 'new' anymore because every object that needs to be long-lived will be stored in containers of this class. You will be able to take references and pointers to these objects very safely because you will have a much better control on the lifespan of each object.
The trick is how to allocate and share the instance of your state class. There are multiple ways, choose the one you like:
- Declaring it in a global container. Simple, efficient, producing very fast code. But you won't be able to have more than one state. In practice it's rarely a problem. You may even circumvent it by declaring a list (or a map) of states.
- Declaring it as a local to the "main" function and passing a reference to it to all classes and functions that may use it. It may be a bit heavy, but that allows handling multiple concurrent states in a very elegant way and makes all your code 100% stateless ! Nothing is stored in your other classes so they need far less resource management.
- Declaring a static singleton getter in the class itself. That is equivalent to making it a global, it's just more limited in the way the global variable will be allocated.
In theory, deleting this class should release all resources, all memory and all file descriptors used by your application. That pattern is so powerful that you may even consider adding a "save state" feature that will be easy to implement if your state is good enough to restore external resources (e.g. re-open files, re-establish network connections, ...) but that's an added bonus.
... because Java developpers will break things
Never ever use 'new' if you come from Java. You will misuse it sooner or later, you will create leaks. In fact, even in Java you could use other strategies that improve performance and memory usage, but that's another debate. 'new' in C++ cannot be used in a "fire and forget" way like you are used to do, and there are two major reasons:
- 'new' needs to be followed by a matching 'delete', or you will have a leak.
- The destructor will never be called. Destructors are a key part of C++, especially since you are doing RAII (aren't you ?) So there will be resource leaks, which are even more problematic than memory leaks.
Of course, if you are a Java developper learning C++, learn 'new' and use it, then learn enough C++ to avoid it whenever possible. In the meantime, be as careful as possible when using 'new' and 'delete'.