Most computer programs rely on resources provided by the operating system or another software. The most common resource is memory, closely followed by files on the local file system. Be it a chunk of memory or a database connection, resources should be dealt with gently; return them once you no longer need them.
Many text books and university courses on C++ familiarize the programmer with low-level functions such as new and delete. These mechanisms are inconvenient and error-prone since you need to make sure that you release the resource no matter which execution path your program takes. Less obvious paths, for example exceptions in the control flow, tend to be forgotten.
Resource acquisition is initialization
Java programmers laugh at such petty problems, they rely on Java’s garbage collector to clean up after them. Indeed, garbage collection works well for memory. Open files and other resources, however, still have to be dealt with manually, because for these resources deterministic behavior is important. This eventually leads to language features such as the finally keyword, which is used to deal with cleaning up after exceptions in the program flow.
C++ provides a simple, yet powerful mechanism for resource management, commonly referred to as “Resource Acquisition Is Initialization” (RAII). The C++ standard guarantees that the destructor of an object is called whenever it goes out of scope. It does not matter why an object goes out of scope, e.g., due to reaching the end of a block or because an exception has been called, the destructor will always be called. Hence, it is possible (and very advisable) to create specialized resource management classes. In the constructor of such a class, the newly created object assumes ownership of a resource. The object’s destructor releases the resource.
Memory management with shared_ptr
Before we learn how to write resource managing classes ourselves, we take a look at C++11’s standard library feature
std::shared_ptr<Value>
Here, Value is the name of a type of your choosing. You can use any basic type (int, double, …) or a custom class. To create a shared_ptr, it is recommended to use std::make_shared<Value>:
// a custom class class fancy_window { fancy_window(std::string const & window_title, int width, int height) { // some construction stuff } void maximize() { // some code to maximize the window } }; // create a shared pointer of fancy_window // parameters passed to make_shared are forwarded // to the constructor of fancy_window std::shared_ptr<fancy_window> window = std::make_shared<fancy_window>("Main menu", 600, 400);
Since the return type of make_shared is known at compile time, you can use C++11’s auto keyword to reduce typing effort and duplication:
auto window = std::make_shared<fancy_window>("Main menu", 600, 400);
Plain old C pointers and smart pointers
What is a shared_ptr? It is a smart pointer, i.e., an object which behaves much like an ordinary C pointer (fancy_window *), but sprinkled with automatic resource management capabilities. Consider the following operations on our window shared_ptr:
auto same_window = window; // copying fancy_window const & reference_to_window = *window; // dereferencing with * window->maximize(); // dereferencing with ->
These lines would be the same for a standard fancy_window * window. The big difference is what happens when shared_ptr<fancy_window> goes out of scope. When a C pointer goes out of scope, nothing happens. If this is the last pointer pointing to a memory block, the memory block will be lost forever (well, until the program terminates). On the other hand, shared_ptrs know if any other shared_ptrs point to the same memory block. When a shared_ptr goes out of scope, its destructor will be called. The destructor checks if it is the last shared_ptr pointing to the object and, if so, deletes the object. This way, no memory is lost.
Conclusion
shared_ptr is a good example of a general concept: Whenever you deal with resources of any kind, make sure to wrap them in a class which ensures the resources are properly released. Heed this advice. You will find your programs more stable, since you will not leak resources. You will find your code much cleaner, since the actual program logic is separated from resource management code.
Try it, you sure will love it!
No to delete an object is not good, but program in most cases will keep running correctly.
Too early delete an object is bad, as program will throw exception will calling to a deleted object.
But really ugly is using memory (i.e. pointer to a “not object” like string or other), that was already freed (to early), because program will crash later on and problem of crash is unclear.
Projects without a shared_ptr mechanism will spend a lot of time to the problems caused by to early destroyed/freed objects.
You are right, leaking resources is usually not quite as bad as accessing invalid ones. When you access invalid memory you do not even have the guarantee that your program crashes. It is undefined behaviour, meaning your program could appear to run just fine, but produce nonsense. Thanks for reading our blog and caring about resource managment!