C++11 auto_ptr : 4 points why it should not be used ?


C++11 auto_ptr :4 points why it should not be used?

The auto_ptr is a deprecated version of unique_ptr since it has less functionality and is insecure.The unique_ptr was in fact introduced to replace the auto_ptr completely.Although auto_ptr is deprecated and it’s used is not encouraged in your program you may ask why even consider reading about it? Of course it should not be used in your program but it is still worth knowing why it’s used should be inhibited? because only when we know the limitations we can hope to make it better;which actually gave rise to unique_ptr .So,this post will describe some underlying principle of the auto_ptr and eventually get to the main topic -4 points why auto_ptr should not be used.


auto_ptr

The difference between auto_ptr and unique_ptr is unique_ptr does not allow sharing of resources,although auto_ptr also does not allow sharing of resources it allow transfer of resources from one pointer to another.Transfer of resources work in such a way that when one auto_ptr is assigned to another-using copy constructor or assignment operator-the previous auto_ptr is devoid of the resource and is assigned a nullptr.And so after transferring the storage only the new auto_ptr has the ownership of the resource.

auto_ptr<int>ap(new int(789) ) ,
  ap1(ap) ; //storage ownership is transferred

cout<< *ap1 << endl
  << *ap ; //undefined

Since the resource ownership of ap has been transferred to ap1 accessing the value using the “*” operator will give you an undefined result.

In view of the ownership transfer behavior of auto_ptr we can say auto_ptr tends to be a bit irresponsible.After the resource transfer the previous owner is just left as a dangling pointer and you known how this will end up later in your program-unexpected output.This scenario can even let one to conclude that shared_ptr is better than auto_ptr;although it also suffer from several drawbacks.The problem arising from transference of ownership has mainly to do with copy constructor and assignment operator functions because these functions are called when an auto_ptr is assigned or copied.Well then let us see how these functions are defined in auto_ptr and may be try to come up with a solution-not similar as the solution implemented by unique_ptr -which might solve the ownership problem.





Copy constructor and assignment operator functions

If we consider ‘mem’ as a pointer data member of auto_ptr that points to the storage then the simplest version of copy constructor and assignment operator can be defined as shown below.

*note::Every smart pointer has a pointer data member of the type the smart pointer template is declared for.This pointer will point to the storage that is passed during the initialization process,visit this link https://corecplusplustutorial.com/cpp11-shared_ptr-class-internal-workings-with-code-example/ to check out the internal structure of shared_ptr.Although the internal structure given is for shared_ptr the implementation design will remain the same as other smart pointer template;some codes will vary in copy constructor,assignment operator,member functions,etc.

/*copy constructor*/
auto_ptr<T>(const auto_ptr<T> &ap)
:mem( ap.mem ) //ownership transferred
{
  ap.mem=nullptr ; ///important
}

//assignment operator
auto_ptr<T>& operator= (const auto_ptr<T> &ap )
{
if( ap==this )
{
 return *this ;
}

mem=ap.mem ; //ownership transferred
ap.mem=nullptr ; //important

return *this ;
}

If we look at the definition of both the functions,the line “ap.mem=nullptr” after the transfer of ownership is what makes the auto_ptr lose the ownership of the storage.In shared_ptr this line of code is eliminated and in unique_ptr copy constructor and assignment operator functions is deleted.So this line is what actually set apart auto_ptr from shared_ptr and unique_ptr and also the culprit behind various issue auto_ptr renders.

To solve the issue shallow copy can be implemented but shared_ptr already used that method and another method is to make the resource unshareable and unfortunately unique_ptr has made use of that design.The only method that remains is deep copy.I am sure you have heard a lot of things about deep copy:occupy unnecessary resources or inefficient.But here we care more about making the auto_ptr work and less about it’s efficiency.So let’s perform deep copy in the copy constructor and assignment operator functions.

/*copy constructor*/
auto_ptr<T>(const auto_ptr<T> &ap)
:mem( new T(*ap.mem) ) //deep copy
{ }

//assignment operator
auto_ptr<T>& operator= (const auto_ptr<T> &ap )
{
if( ap==this )
{
return *this ;
}

delete mem ; //if the pointer points to any storage
mem=new T(*ap.mem) ; //deep copy

return *this ;
}

int main( )
{
//code exammple
auto_ptr<int>ap(new int(89)) , ap1(ap) ;

*ap=879 ;

cout<< *ap1 << endl //work fine and output 89
 << *ap ; //work fine and output 879

return 0;
}

Now the previous owner also points to some valid memory but mind you there is still the inefficiency issue so you better be careful.


Member functions of auto_ptr

All the meber functions of auto_ptr are almost the same with unique_ptr
member functions.And some of it’s member functions are given below:

i)get( )

ii)reset( )

iii)release( )

iv)operator*

v)operator=

vi) operator auto_ptr

vii)operator auto_ptr_ref

Please visit the unique_ptr post to know how these functions work.


 



4 points why auto_ptr should not be used.

4 points to support why auto_ptr is deprecated and why it should not be used is given below.

First::Error prone.

auto_ptr is error prone.There are various ways which one might end up in a conundrum situation when using auto_ptr.The foremost issue arising from using an auto_ptr is pointer left as dangling pointers.The simplest instance of this case is also shown in the first program of this post.There the auto_ptr ‘ap‘ is left as dangling pointer and so using it to access the value will give you an undefined result.Another instance where the pointer is left as dangling pointer is when the auto_ptr is passed to the function as an argument.Consider the code below.

void func(auto_ptr<int> arg)
{
  cout<< *arg ;
}

int main( )
{
auto_ptr<int> auPtr(new int(89) ) ;

func(auPtr) ;

int ii=*auPtr ; ///undefined

cin.get( ) ;
return 0 ;
}

When auPtr is passed to the function func( ) the storage held by the auPtr will be transferred to the func() argument arg.So after calling the function auPtr points to no valid memory and assigning it’s value to the variable ‘ii’ is undefined.

 

Second::auto_ptr does not support array.

When it comes to array even the shared_ptr does not support it.You may think that by allocating an array storage during the dynamic storage allocation the auto_ptr or shared_ptr will hold responsibility of the array storage like the normal raw pointer would.But it is not true,if you allocate an array storage the pointer may be able to access the first value but not the second or the third or the remaining elements.And also when the auto_ptr goes out of scope it will delete only the storage of the first element because auto_ptr uses ‘delete’ operator not ‘delete []’ operator to delete the storage.So the code like the instances shown below is dangerous.

{
auto_ptr<int>ap(new int[2]{ 45,696 } ) ;

} //deletes only the storage of 45

When using smart pointer if the need for an array arises then use unique_ptr.By using unique_ptr you can access all the array element like you would when using the normal array and also the array storage is deleted safely when the pointer goes out of scope.

unique_ptr<int[]> up(new int[3]{ 34, 5 , 6} );

cout<< up[0] << ” ” << up[1] << ” ” << up[2] ;

 

Third::auto_ptr cannot be used with STL containers.

I have read many C++11 tutorial which claim that auto_ptr cannot be used with STL containers but none have shown how and why? but here I’ll show you everything.The reason is pretty simple.We know that when an auto_ptr is copied to another auto_ptr the right side argument auto_ptr loses it’s storage.Now this can be problematic for STL containers.Consider an auto_ptr that hold a vector that can contain an int type as it’s template argument.When this auto_ptr is copied to another auto_ptr of the same type.The result is disastrous.

auto_ptr< vector<int> > atVec(new vector<int> {8,9 } ) ,
 atVec1(atVec) ;

cout<< atVec1[0] << atVec1[1] << endl
  << atVec[0] << endl //undefine
  << atVec[1] ; //undefine

After the copy operation the vector of the auto_ptr atVec is completely empty but we do not want that.We simply want the second vector to contain the same value as the first but instead of doing that the auto_ptr transferred the whole storage leaving nothing for the first.This is completely outrageous but this is what you will get for using auto_ptr and don’t blame yourself blame the auto_ptr.So in order not to put yourself into such situation you should either use unique_ptr or shared_ptr;but always try to go with the first one if possible.

 

Fourth::auto_ptr does not support move()

The ‘moving of object’ or ‘stealing of object’ feature introduced in C++11 is an efficient way of copying and assigning an object.The unique_ptr is meant to replace the auto_ptr and so unfortunately auto_ptr does not support moving of object.Which means the use of unique_ptr must be encouraged and the use of auto_ptr discouraged.Faster working code is one benefit which you will gain by using the ‘move’ feature included unique_ptr.


Related Link

->Unique_ptr: what makes it unique?
 
->Smart pointer : Shared pointer ; why is it call shared pointers?