Modern C++ has everything you need for this. The days when such code with pointers and exceptions was dangerous are gone now:
throwstd::exception();// leak of ptr.
Certainly you can deallocate ptr before throwing exception, but more often programmer just might miss it or be not aware of throwing exception from some function or method. Indeed, even new operator throws std::bad_alloc when it can't allocate requested amount of memory. So in this article I am going to show you how to stay calm with such issues.
First of all – RAII
or RAII is everything
Resource Acquisition Is Initialization (RAII) is a programming idiom for automatic resource initializing and destroying it. In C++ it is implemented through constructor and destructor of class. Thereby if we implement wrapper which manages allocation and deallocation of Resource instance in my first example and create it on stack, we solve our issue with possible leaks. See the following wrapper and its usage:
throwstd::exception();// destructor of wrapper is called here.
}// or here.
Here RAII helps us to manage lifetime of resource which is allocated on heap, because of throwing exception, compiler generates code which calls destructors of local variables. Thus ResourceWrapper destroys Resource instance anyway. Also C++ standard guarantees that destructor of class is called only if constructor of class is performed normally (without exceptions). It saves us from invalid delete operator invocation in destructor if allocation of Resource instance throws std::bad_alloc.
But my ResourceWrapper isn't ideal, because it doesn't handle copy and assign operations well. As a solution I forbade such actions. I told compiler not to generate copy constructor, move constructor (C++11) and assign operator with help =delete from C++11. Without C++11 it can be done through declaration those methods in private section without implementation, linker will show errors on such actions. But I prefer C++11 way because it is clearer.
So this is RAII idiom. Let’s see what points of C++ standard library you can use for such cases.
std::auto_ptr<T> and single objects
or it is not dress for all times
There are still lots of projects that don't use C++11, so let’s talk about std::auto_ptr<T> at first. It is deprecated in the last standard, but it is still common for lots of people.
std::auto_ptr<T> provides RAII similar to wrapper which I have shown above. With the difference that std::auto_ptr<T> obtains pointer in its constructor or std::auto_ptr<T>::reset(T*) method.
Common cases of use for this entity are:
wrap some resource on heap as local variable or field of class for automatic managing of its destruction:
But beware of other cases of use, because operator = and copy constructors of std::auto_ptr<T> transfer it value to left hand side std::auto_ptr<T>. It is very easy to get UB (undefined behavior, probably will be segmentation fault). The following code demonstrates it:
resource2->doSomething();// Everything is fine.
}// ptr is freed here in resource2 destructor.
Because of such behavior you should not use std::auto_ptr<T> in containers, e.g. std::vector<T>. They can use copying and assignment which will break your code. Also keep in mind that std::auto_ptr<T> is not smart pointer which has reference counting.
For accessing managed object of std::auto_ptr<T> you can use:
As you can see there are a lot of choices, but generally you will use -> to access managed object.
Last methods of std::auto_ptr<T> which I haven’t mentioned before are:
std::auto_ptr<T>::reset(T*) which resets managed instance to passed and releases the old one
// ptr is freed and resource manages new instance.
std::auto_ptr<T>::release() which returns managed instance and forgets about it.
// resource doesn't hold anything, but ptr is still alive.
One more issue with std::auto_ptr<T> in that it suits only for single objects, because it calls operator delete in its destructor. You can't keep arrays in it. If you try doing it, you will face UB. It will likely leak or cause segmentation fault. See:
For allocating an array of objects you must use std::vector<T> and some tricks with it.
std::vector<T> and arrays
or std::vector<T> is more than you think
Standardized implementation of std::vector<T> says that std::vector<T> is a sequence container and its elements are stored contiguously. It means that if user obtains address of the first element in std::vector<T> it will be the same as a pointer to dynamic array, so it has same behavior as usual pointer to dynamic array. Also std::vector<T> can reserve necessary amount of memory to store data through method std::vector::resize(std::size_t). As a result you can use it like dynamic array. Check it out:
In such way function handleDynamicArray() will work like with usual dynamic array, but in fact its memory is managed by std::vector<T> and memory will be emptied when std::vector<T> goes out of scope. You can forget about deallocation of array memory and be saved from throwing exceptions.
As for question about how to handle lifetime of dynamic array if it is obtained from other function\method. Again we can change std::vector<T> to necessary size, use std::copy() for copying array content and free original array. See:
Maybe it looks a little bit strange and not efficient because of extra copying (std::copy), but mostly it is better than managing dynamic arrays manually.
For two-dimensional array you can use std::vector<T> in std::vector<T>, like this:
Unfortunately you won't be able to pass it as int** to some function\method, but you still can copy obtained int** array via std::copy function. If you are really interested in matrices, you should better try to search some library with such container or implement your own in RAII way.
In this article I have described RAII idiom in C++ language. There are reviewed ways of avoiding usage of pointers without C++11 standard with help of std::auto_ptr<T> and std::vector<T>. Article provides good explanation for each case of their usage. In the next article I will review what C++11 standard has for accomplishing goal of my topic.
As always you can find examples of code in my github repository.
Enjoyed this article?
you might want to subscribe for our newsletter to get more content like this: