Introduction Because of lack of knowledge in dealing with pointers or absence of clue in how to work without them, very often people scold C++ as language. After my two previous articles about references and pointers “Do not muddle references and pointers” and “Extended talk about references and pointers” I am going to tell how to avoid usage of pointers. Modern C++ has everything you need for this. The days when such code with pointers and exceptions was dangerous are gone now:
void doIncredibleBadCode(bool badMood)
{
Resource *ptr = new Resource();
if (badMood)
{
throw std::exception(); // leak of ptr.
}
delete 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:
class ResourceWrapper
{
public:
ResourceWrapper()
{
this->ptr = new Resource();
}
~ResourceWrapper()
{
delete ptr;
}
// forbidden operations.
ResourceWrapper(const ResourceWrapper&)=delete;
ResourceWrapper(const ResourceWrapper&&)=delete;
const ResourceWrapper& operator =(const ResourceWrapper&)=delete;
private:
Resource *ptr;
};
void doSaferCode(bool goodMood)
{
ResourceWrapper wrapper;
if (!goodMood)
{
throw std::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 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 at first. It is deprecated in the last standard, but it is still common for lots of people. std::auto_ptr provides RAII similar to wrapper which I have shown above. With the difference that std::auto_ptr obtains pointer in its constructor or std::auto_ptr::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:
void doGoodCode(bool mood)
{
std::auto_ptr resource(new Resource());
if (!mood)
{
throw std::exception(); // resource is freed here.
}
} // or here.
* pass allocated instance as wrapped argument to function or method
```
void passAutoPtr(std::auto_ptr<Resource> resource)
{
// Do something with resource.
} // resource is freed here.
...
passAutoPtr(std::auto_ptr<Resource>(new Resource()));
return allocated instance as wrapped value from function or method
std::auto_ptr getAutoPtr()
{
return std::auto_ptr(new Resource());
}
std::auto_ptr resource = getAutoPtr();
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:
{
Resource *ptr = new Resource();
std::auto_ptr resource0;
resource0.reset(ptr); // ptr belongs to resource0.
// ptr belongs to resource1, but not resource0.
std::auto_ptr resource1 = resource0;
// ptr belongs to resource2, but not resource1.
std::auto_ptr resource2(resource1);
resource2->doSomething(); // Everything is fine.
resource1->doSomething(); // UB.
resource0->doSomething(); // UB.
} // 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:
* std::auto\_ptr<T>::operator ->
resource->doSomething();
* std::auto\_ptr<T>::operator \*
(*resource).doSomething();
* method std::auto\_ptr<T>::get()
resource.get()->doSomething();
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
```
Resource *ptr = new Resource();
std::auto_ptr<Resource> resource(ptr);
// ptr is freed and resource manages new instance.
resource.reset(new Resource());
std::auto_ptr::release() which returns managed instance and forgets about it.
std::auto_ptr resource(new Resource());
// resource doesn't hold anything, but ptr is still alive.
Resource *ptr = resource.release();
delete ptr;
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:
{
std::auto_ptr array(new Resource[10]);
} // Something bad will happen here.
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:
void handleDynamicArray(const int *array, std::size_t size)
{
// Do something with array.
}
std::size_t size = 10;
std::vector array;
array.resize(size);
handleDynamicArray(&array[0], array.size());
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:
int* returnDynamicArray(const std::size_t size)
{
return new int[size];
}
int *ptr = returnDynamicArray(size);
std::vector array;
array.resize(size);
std::copy(ptr, ptr + size, &array[0])
delete []ptr;
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:
typedef std::vector<std::vector> Matrix;
Matrix matrix;
matrix.resize(size);
for (std::size_t i = 0; i < matrix.size(); ++i)
{
matrix[i].resize(size);
}
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. Conclusion 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 and std::vector. 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.