C + + Smart pointers

Source: Internet
Author: User

Smart pointer template class

A smart pointer is a class object that behaves like a pointer, but this object has additional functionality. This article describes three only pointer templates that can help you manage dynamic memory allocations. First look at what features are needed and how they are implemented. Take a look at the following function

void Remodel (std::string & str)

{

std::string *ps = new std::string (str);

...

str = PS;

return;

}

You may have found a flaw in it. Whenever called, the function allocates memory in the heap, but never recycles, causing a memory leak. You may also know the solution------just don't forget to add the following statement before the return statement to release the allocated memory:

Delete PS;

However, it is seldom the best to deal with the "don't forget" approach. Because you may sometimes forget, sometimes you may remember, but you may inadvertently delete or comment on the code. Even if you do not forget, there may be problems. Take a look at the following variants:

void Remodel (std::string & str)

{

std::string *ps = new std::string (str);

...

if (weird_thing ())

{

Throw exception ();

}

str = *PS;

Delete PS;

Return

}

When an exception occurs, the delete will not be executed and therefore will cause a memory leak.

When a function such as remodel () terminates (whether it is terminated normally or terminated due to an exception), the local variable is removed from the stack memory------so the memory occupied by the pointer PS is freed. If PS points to the memory is also released, that is how good ah. If PS has a destructor, the destructor frees the memory he points to when PS expires. So the problem with PS is that it's just a regular pointer, not a class object with destructors. If it is an object, you can have its destructor delete the pointed memory when the object expires. This is the thought behind Auto_ptr, Unique_ptr and shared_ptr. Template auto_ptr is a solution provided by c++98, which has been discarded by C + + and provides two additional solutions. However, although Auto_ptr is abandoned, it has been used for many years at the same time, if your compiler does not support the other two solutions, Auto_ptr will be the only option.

Using smart pointers

Each of these three smart pointer templates (AUTO_PTR,UNIQUE_PTR,SHARED_PTR) defines a pointer-like object that can be assigned a new (direct or indirect) address to the object. When a smart pointer expires, its destructor uses Delete to free memory. Therefore, if you assign the address returned by new to these objects, you do not have to remember to release the memory later, and the memory will be freed automatically when the smart pointer expires. The differences in behavior between auto_ptr and regular pointers are explained, and shared_ptr and unique_ptr behave the same as auto_ptr.

To create a smart pointer object, you must include the header file memory, which is defined by the file template. Then use the usual template syntax to instantiate a pointer of the desired type. For example, the template auto_ptr contains the following constructors:

Template<class x>

Class Auto_ptr

{

Explicit Auto_ptr (X *p = 0) throw ();

...

};

throw () means that this constructor does not throw an exception: like Auto_ptr, throw () is also discarded. Therefore, a auto_ptr that requests an X type will get a auto_ptr that points to the X type:

AUTO_PTR<DOUBLE>PD (new double);//pd an auto_ptr to double

(use in place of double *PD)

Auto_ptr<string>ps (new string);//ps an auto_ptr to string

(use in place of string *ps)

The new double is a pointer to the newly allocated memory block. It is the argument of the constructor auto_ptr<double>, which corresponds to the argument p in the prototype. Similarly, the new string is also an argument to the constructor. The other two kinds of smart pointers use the same syntax:

Unique_ptr<double> PDU (new double);//pdu an unique_ptr to double

Shared_ptr<string> PSS (new string);//pss a shared_ptr to string

So. To convert the remodel () function, proceed as follows in 3 steps:

    1. Contains the head file memory;
    2. Replaces a pointer to string with a smart pointer object pointing to a string;
    3. Deletes the DELETE statement.

The following is the result of modifying the function using Auto_ptr:

#include <memory>

void Remodel (std::string & str)

{

Std::auto_ptr<std::string>ps (new std::string (str));

...

if (weird_thing ())

{

Throw exception ();

}

str = *PS;

Delete PS; NO Longer NEEDED

return;

}

Notice that the smart pointer template is in the namespace Std. Here is a simple program that demonstrates how to use all three smart pointers. Each smart pointer is placed inside a block of code so that the pointer expires when it leaves the code block. The report class uses methods for reporting object creation and destruction.

Smrtptrs.cpp--using three kinds of smart pointers

Requires support of c++11 shared_ptr and Unique_ptr

#include <iostream>

#include <string>

#include <memory>

using namespace Std;

Class Report

{

Public

Report (const string s): Str (s)

{

cout << "Object created!" << Endl;

}

~report ()

{

cout << "Object deleted!" << Endl;

}

void comment () const

{

cout << str << Endl;

}

Private

String str;

};

int main ()

{

{

Auto_ptr<report> PS (new report ("Using auto_ptr"));

Ps->comment ();

}

{

Shared_ptr<report> PS (new report ("Using shared_ptr"));

Ps->comment ();

}

{

Unique_ptr<report> PS (new report ("Using Unique_ptr"));

Ps->comment ();

}

System ("pause");

return 0;

}

Program Output Result:

All smart pointer classes have a explicit constructor that takes the pointer as a parameter . Therefore, you do not need to automatically convert the pointer to a smart pointer object:

Shared_ptr<double> PD;

Double *p_reg = new Double;

PD = P_reg; Not allowed (implicit conversion)

PD = Shared_ptr<double> (P_reg);//allowed (Explicit conversion)

shared_ptr<double> pshared = P_reg;//not allowed (implicit conversion)

Shared_ptr<double> pshared (P_reg);//allowed (Explicit conversion)

Because of how smart pointer template classes are defined, many aspects of smart pointer objects are similar to regular pointers. For example, if PS is a smart pointer object, you can dereference it (*ps), use it to access a struct member (Ps->puffindex), assign him to a regular pointer to the same type, and assign a smart pointer object to another type of smart pointer object. But it will cause a problem, which will be discussed below.

But before we do, let's start by saying something that should be avoided for all three smart pointers:

String Vacation ("I wandered lonely as a cloud");

Shared_ptr<string> PVAc (&vacation);//no;

When PVAc expires, the program will use the delete operator for non-heap memory, which is an error.

Considerations for Smart pointers

First look at the following assignment statement:

Auto_ptr<string> PS (New string ("I reigned lonely as a cloud");

auto_ptr<string> Vocation;

Vocation = PS;

What do the above assignment statements do? If PS and vocation are regular pointers, then two pointers will point to the same string object, which is unacceptable because the program will attempt to delete the same object two times-once the PS expires, once the vocation expires. There are several ways to avoid this problem.

L Define the assignment operator so that it performs a deep copy. This way two pointers will point to different objects, one of which is a copy of the other object.

L establish the concept of ownership (ownership), for a specific object, there can only be one smart pointer to it, so only the ownership and unique_ptr strategy , but Unique_ptr's strategy is more stringent.

L Create a smarter pointer that tracks the number of smart pointers that reference a particular object. This is called a reference count (reference counting). This is the strategy adopted by shared_ptr.

Of course, the same strategy applies to copy constructors as well.

Each method has its purpose. The following programs are not suitable for use with auto_ptr instances

#include <iostream>

#include <string>

#include <memory>

using namespace Std;

int main ()

{

auto_ptr<string> Films[5] =

{

Auto_ptr<string> (New String ("Fowl Balls")),

Auto_ptr<string> (New string ("Duck Walks")),

Auto_ptr<string> (New String ("Chicken Run")),

Auto_ptr<string> (New String ("Turkey Errors")),

Auto_ptr<string> (New String ("Goose Eggs"))

};

Auto_ptr<string> Pwin;

Pwin = films[2];

cout << "The nominees for best avian baseball film is \ n";

for (int i = 0; i < 5; i++)

{

cout << *films[i] << Endl;

}

cout << "The winner is" << *pwin << Endl;

Cin.get ();

return 0;

}

The output of the program:

The problem here is that the following statement ownership is transferred from Film[2] to Pwin:

Pwin = films[2];//films[2] loses ownership

This causes films[2] to no longer reference the string. After auto_ptr discards the ownership of an object, it may be used to access the object. When the program printed films[2] pointed to the string, it was found that it was a null pointer, which was obviously nasty to the unexpected.

If you use shared_ptr instead of auto_ptr in the above program, the program will run normally with the following output:

This time Pwin and films[2] point to the same object, and the reference count increases from 1 to 2. At the end of the program, Pwin first calls its destructor, which reduces the reference technology to 1. Then, the members of the shared_ptr array are freed, and when the destructor is called on filmsp[2], the reference count is lowered to 0 and the previously allocated space is freed.

What happens if I use the unique_ptr result? Unique_ptr also uses the ownership model, but when unique_ptr is used. The program does not wait for the run phase to crash, but a syntax error, as follows

Pwin = films[2];

Why Unique_ptr is better than auto_ptr

Take a look at the following statement:

auto_ptr<string> P1 (New string ("Auto"));//#1

Auto_ptr<string> p2;//#2

P2 = p1;//#3

In statement # #,p2 takes over ownership of a string object, and P1 's ownership is stripped. As I said earlier, this is a good thing to prevent P1 and P2 destructors from trying to delete the same object , but it would be a bad thing if the program subsequently tried to use P1, because P1 no longer points to valid data.

Let's look at the use of unique_ptr:

Unique_ptr<string> P3 (New string ("Auto"));//#4

Unique_ptr<string> p4;//#5

P3 = p4;//#6

The compiler considers the statement to be illegal, avoiding the problem that P3 no longer points to valid data. Therefore,unique_ptr is more secure than Auto_ptr ( a compile-time error is more secure than a potential program crash).

But sometimes a smart pointer is assigned to another hanging pointer that does not leave a danger. The following function definitions are assumed:

Unique_ptr<string> Demo (const char *s)

{

Unique_ptr<string> Temp (new string (s));

return temp;

}

And suppose you write the following code:

Unique_ptr<string> PS;

PS = Demo ("Uniquely Special");

The demo () returns a temporary UNIQUE_PTR, and then PS takes over all the objects that were originally returned to the Unique_ptr, and the returned Unique_ptr is destroyed. This is fine, because PS has ownership of the string object. But another advantage here is that the temporary Unique_ptr returned by the demo () is soon destroyed, and there is no chance to use it to access invalid data. In other words, there is no reason to prohibit such assignments. Amazingly, the compiler does allow this assignment!

In short, the program tries to tell when a unique_ptr is assigned to another, and if the source Unique_ptr is a temporary rvalue, the compiler allows to do so; if the source Unique_ptr will exist for some time, the compiler will prohibit this :

          unique_ptr<string> pu1 (New String ("Hi ho!"));

         unique_ptr<string> pu2;

         pu2 = pu1;                               //#1 not allowed

         unique_ptr<string > pu3;

         pu3 = unique_ptr<string> (New string ("yo!"));    #2 allowed

The statement # # will leave the suspended Unique_ptr (PU1), which can cause harm. The statement # # does not leave the unique_ptr because it calls the Unique_ptr constructor, which creates a temporary object that is destroyed after its ownership is transferred to PU3. This sui-like behavior suggests that unique_ptr is better than allowing two auto_ptr to be assigned. This is also forbidden ( Just a suggestion that the compiler does not forbid) to use auto_ptr in the container object, but allows the use of unique_ptr the reason . if the container algorithm attempts to perform an operation similar to statement # # to a container containing unique_ptr, it will result in a compilation error, and if the algorithm tries to perform an operation similar to statement # #, there will be no problem, and for auto_ptr, it is similar to the statement # 1 of operations can lead to indeterminate behavior and mysterious crashes.

Of course, you might actually want to do something like statement # #. This assignment is unsafe only when you use abandoned smart pointers, such as dereferencing, in a non-intelligent manner. to safely re-enter this pointer, you can assign a new value to it. C + + has a standard library function std:: Move (), allowing you to assign one unique_ptr to another. Here is an example of using the demo () function, which returns a Unique_ptr<string> object:

Unique_ptr<string> PS1, PS2;

PS1 = Demo ("Uniquely Special");

PS2 = Move (PS1);

PS1 = Demo ("and more");

cout << *ps2 << *ps1 << Endl;

There is another advantage compared to auto_ptr,unique_ptr. It has a variant that can be used for arrays. Remember that you must pair the delete and new with the Delete [] and new [] pairs. Template auto_ptr uses delete instead of delete[], so it can only be used with new, not with new[]. However, UNIQUE_PTR has versions that use new [] and delete []:

unique_ptr<double []>pda (New Double (5));//will use Delete []

Warning: You cannot use auto_ptr and shared_ptr when allocating memory using new, you cannot use them when allocating memory using new [], you cannot use auto_ptr or shared_ptr when allocating memory without new, and when you do not allocate memory with new [] You cannot use Unique_ptr.

C + + Smart pointers

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.