C ++ smart pointer

Source: Internet
Author: User

C ++ smart pointer
Direct Memory ManagementWhen to directly manage

In short, when the memory is allocated to the stack, it does not need to be directly managed. When the memory is allocated to the stack, it needs to be manually recycled, or the automatic recovery mechanism is triggered when the memory allocation on the stack is full.
The memory occupied by a C/C ++ compiled program is divided into the following parts:

Stack: the stack is automatically allocated and released by the compiler, storing the parameter values of functions, and the values of local variables. The operation method is similar to the stack in the data structure. Heap: Generally, it is assigned and released by the programmer. If the programmer does not release the heap, it may be recycled by the OS at the end of the program. Note that it is different from the heap in the data structure. The allocation method is similar to the linked list. Global (static) -- the storage of global and static variables is put together, and the initialized global and static variables are in one area, uninitialized global variables and uninitialized static variables are in another adjacent area. The program is released by the system. In the text constant area, the constant string is placed here, and is released by the system after the program ends. Code area-stores the binary code of the function body. Example Program

This is written by a senior. It is very detailed.

// Main. cpp int a = 0; global initialization zone char * p1; Global uninitialized Zone main () {int B; stack char s [] = "abc"; stack char * p2; stack char * p3 = "123456"; 123456/0 is in the constant zone, and p3 is in the stack. Static int c = 0; Global (static) initialization zone p1 = (char *) malloc (10); p2 = (char *) malloc (20 ); the allocated 10-byte and 20-byte areas are in the heap area. Strcpy (p1, "123456"); 123456/0 is placed in the constant area, and the compiler may optimize it to the "123456" that p3 points. }

Note: In addition to the above malloc, the memory allocated by new also needs to be manually destroyed in the heap.

Dynamic Memory

We can see from the above that the memory allocated to the stack needs to be manually allocated and released dynamically. We call it dynamic memory. In C ++, dynamic memory is allocated and released through new and delete.
New: Allocate space for the object in dynamic memory and return a pointer to the object. We can choose to initialize the object.
Delete: accepts a pointer to a dynamic object, destroys the object, and releases the associated memory.
The memory allocated in free space is unknown, so new cannot name the assigned object, but returns a pointer to the object.

Int * pi = new int; // pi indicates a dynamically allocated uninitialized unknown object.

You can use the direct initialization method to initialize a dynamically allocated object.

int *pi=new int(1024);string *ps=new string(10,’9’);

You can also initialize the value of the dynamically allocated object by following a pair of empty parentheses after the type name:

String * ps1 = new string; // The default Initialization is empty stringstring * ps = new string (); // The value is initialized as a Null string

Dynamically allocate a const object
Similar to any other const object, a dynamically assigned const object must be initialized. For a class that defines the default constructor, its const dynamic object can be implicitly initialized, while other types of objects must be explicitly initialized.

Memory depletion

Once a program uses up all its available memory, the new expression will fail. By default, if new cannot allocate the required memory space, it will throw an exception of the type bad_alloc. We can change the new method to prevent him from throwing an exception:

Int * p1 = new int; // if the allocation fails, new throws std: bad_allocint * p2 = new (nothrow) int // if the allocation fails, new returns a null pointer.
Release dynamic memory
Delete p; // p must point to a dynamically allocated object or a null pointer

Releasing a memory that is not newly allocated, or releasing the same pointer value for multiple times, is undefined.

Int I, * pil = & I, * pi2 = nullptr; double * pd = new double (33), * pd2 = pd; delete I; // error: I is not a pointer delete pil; // undefined: pil points to a local variable delete pd; // delete pd2 correctly; // undefined: the memory pointed to by pd2 has been released delete pi2; // correct: releasing a null pointer is always correct.

Dynamic Objects managed by built-in pointer types exist until they are explicitly released.
After the delete operation, the pointer becomes an empty pointer, that is, a pointer to a memory that has previously stored data objects but is invalid.
If we need to retain the pointer, we can assign the nullptr to the pointer after the delete operation so that the pointer does not point to any object.

Smart pointer

An important difference between a smart pointer and a conventional pointer is that it is responsible for automatically releasing the objects to which it points. The difference between the two smart pointers lies in the way of managing underlying pointers:
Shared_ptr allows multiple pointers to the same object, while unique_ptr is an exclusive object. The standard library also defines a companion class named week_ptr, which is a weak reference pointing to the objects managed by shared_ptr. All three types are defined in the memory header file.

Operations supported by shared_ptr and unique_ptr
Operation Description
shared_ptr sp ,unique_ptr up A null smart pointer can point to an object of the T type.
P Use p as a condition judgment. If p points to an object, it is true.
* P Returns the object to which p points.
P-> mem Equivalent to (* p). mem
P. get () Returns the pointer stored in p. Be careful when using this function. If the smart pointer releases its object, the object pointed to by the returned pointer disappears.
Swap (p, q), p. swap (q) Swap pointers in p and q
Shared_ptr class

When creating a smart pointer, you must provide additional information-the type that the pointer can point.

shared_ptr
  
    p1;shared_ptr
   
    > p2;
   
  
Exclusive shared_ptr operations
Operation Description
make_shared (args) Returns a shared_ptr pointing to an object of the dynamically allocated type T. Use args to initialize this object
shared_ptr p(q) P is the copy of shared_ptr q. This operation increments the counter in q. Pointers in q must be converted to T *
P = q Both p and q are shared_ptr, And the stored pointers must be converted to each other. This operation will decrease the reference count of p and increase the reference count of q. If the reference count of p is changed to 0, the original memory managed by p will be released.
P. unique () If p. use_count () is 1, true is returned; otherwise, false is returned.
P. use_count () Returns the number of smart pointers of objects shared with p. It may be slow and is mainly used for debugging.
Make_shared Function

The safest way to allocate and use dynamic memory is to call a standard library function named make_shared. This function allocates an object in the dynamic memory and initializes it. It returns the shared_ptr pointing to this object.

shared_ptr
  
    p3=make_shared
   
    (42);auto p6=make_shared
    
     >();
    
   
  

Copy and assign values of shared_ptr
When copying or assigning values, each shared_ptr records how many other shared_ptr points to the same object.
Shared_ptr automatically destroys Managed Objects
The shared_ptr destructor will decrease the reference count of the object it points. If the reference count is 0, the shared_ptr destructor destroys the object and releases the memory occupied by it.
Shared_ptr also automatically releases the associated memory.
Return increments the number of times the shared_ptr pointer is referenced.
Classes that use resources with a dynamic life cycle
The program uses dynamic memory for one of the following reasons:
1. The program does not know how many objects it needs
2. The program does not know the exact type of the required object
3. The program needs to share data among multiple objects

Shared_ptr and new

The smart pointer constructor that accepts pointer parameters is explicit. We cannot implicitly convert a built-in pointer to a smart pointer. We must initialize a smart pointer in the form of direct initialization:

Shared_ptr
  
   
P1 = new int (1024); // error shared_ptr
   
    
P2 (new int (1024); // The shared_ptr is correct.
    
     
Clone (int p) {return new int (p); // error} shared_ptr
     
      
Clone (int p) {return shared_ptr
      
        (New int (p); // correct}
      
     
    
   
  

Define and change other shared_ptr Methods

Operation Description
shared_ptr p(q) The built-in pointer q points to the object. q must point to the memory allocated by new and can be converted to the T * type.
shared_ptr p(u) P takes over the ownership of the object from unique_ptr u; Sets u to null
shared_ptr p(q,d) P takes over the ownership of the object pointed to by the built-in pointer q. Q must be converted to the T * type. P will replace delete with the callable Object d.
shared_ptr p(p2,d) P is a copy of shared_ptr p2. The only difference is that p will replace delete with the object d.
P. reset () p, reset (q) p, reset (q, d) If p is the shared_ptr that uniquely points to its object, the reset will release the object. If the optional parameter q is passed, p points to q. Otherwise, p is left empty. If the parameter d is also passed, the system will call d instead of delete to release q.

Do not mix common pointers and smart pointers

void process(shared_ptr
  
    ptr){}
  

The parameters of process are passed by passing values. Therefore, the real parameters are copied to ptr. Copying a shared_ptr increases the reference count. Therefore, during the process, the reference count value must be at least 2. when the process ends, the reference count of ptr will decrease, but it will not change to 0. therefore, when the local variable ptr is destroyed, the memory to which the ptr points will not be released.
The correct method is to pass it a shared_ptr:

Shared_ptr
  
   
P (new int (42); // The reference count is 1 process (p );//
  

Although it cannot be passed to process, it can be passed to process with a (temporary) shared_ptr, which is explicitly constructed with a built-in pointer. However, this may cause errors:

Int * x (new int (1024); // danger: x is a common pointer, not a smart pointer process (x); // process (shared_ptr) Error
  
   
(X); // valid, but the memory will be released int j = * x; // undefined: x is an empty hanging pointer
  

Do not use get to initialize another smart pointer or assign a value to the smart pointer.
The smart pointer type is a function named get. It returns a built-in pointer pointing to an object managed by the smart pointer. This function is designed for such a situation: we need to pass a built-in pointer to code that cannot use smart pointers. The code that uses the pointer returned by get cannot delete this pointer.

Shared_ptr
  
   
P (new int (42); // The reference count is 1int * q = p. get (); // correct: but when using q, be careful not to release the pointer it manages {// undefined: two independent shared_ptr points to the same memory shared_ptr
   
    
(Q) ;}// when the block ends and q is destroyed, the memory to which it points is released int foo = * p; // undefined: the memory pointed by p has been released
   
  
Smart pointers and exceptions

If a smart pointer is used, even if the block ends too early, the smart pointer class can ensure that it is released when the memory is no longer needed.
Smart pointers and dummy classes
Use our own release operations
To correctly use smart pointers, we must adhere to some basic rules:

Multiple smart pointers are not applicable to the same built-in pointer initialization (or reset. Pointer returned without deleting get. Do not use get () initialization or reset another smart pointer. If you use the pointer returned by get (), remember that when the last corresponding smart pointer is destroyed, your pointer becomes invalid. If the resource you use smart pointer to manage is not the memory allocated by new, remember to pass it to a deleteer. Unique_ptr

A unique_ptr "owns" the object to which it points. Unlike shared_ptr, only one unique_ptr can point to a given object at a given time point. When unique_ptr is destroyed, the object it points to is also destroyed.
Unlike shared_ptr, no standard library function similar to make_shared returns a unique_ptr. When we define a unique_ptr, We need to bind it to a pointer returned by new. When shared_ptr is killed, unique_ptr must be initialized directly:

Unique_ptr
  
   
P1; // you can point to a double unique_ptrunique_ptr.
   
    
P2 (new int (42); // p2 points to an int with a value of 42
   
  

Because a unique_ptr has the object to which it points, unique_ptr does not support normal copy or assignment operations.
Unique_ptr operation

Operation Description
unique_ptr u1 ,unique_ptr u2 The null unique_ptr can point to an object of the T type. u1 uses delete to release its pointer. u2 uses a callable object of the B type to release its pointer.
unique_ptr u(d) Null unique_ptr, pointing to the object of the T type, replacing delete with the object D of the d type
U = nullptr Release the object pointed to by u and set u to null
U. release () U gives up control of pointer, returns pointer, and sets u to null
U. reset () Release the object pointed to by u
U. reset (q), u. reset (nullptr) If the built-in pointer q is provided, the u is directed to this object; otherwise, the u is set to null.
// Transfer ownership from p1 to p2unique_ptr
  
   
P2 (p1.release (); // release set p1 to null unique_ptr
   
    
P3 (new string ("Trex"); // transfers ownership from p3 to p2p2. reset (p3.release (); // The reset releases the memory that p2 originally points
   
  

Pass the unique_ptr parameter and return the unique_ptr
The rule that cannot copy unique_ptr has one exception: We can copy or assign a value to the unique_ptr to be destroyed. The most common example is to return a unique_ptr from the function.
Pass the deleteer to unique_ptr

Weak_ptr

Weak_ptr points to an object managed by a shared_ptr. Binding A weak_ptr to a shared_ptr does not change the reference count of shared_ptr.
Weak_ptr

Operation Description
weak_ptr w Null weak_ptr can point to an object of the T Type
weak_ptr w(sp) And shared_ptr sp point to weak_ptr of the same object. T must be converted to the type pointed to by sp
W = p P can be a shared_ptr or weak_ptr. Share objects with p w after assignment
W. reset () Set w to null
W. use_count () Number of shared_ptr objects shared with w
W. expired () If w. use_count () is 0, true is returned; otherwise, false is returned.
W. lock () If expired is true, a blank shared_ptr is returned; otherwise, a shared_ptr pointing to the w object is returned.
Dynamic Array

The new and delete operators allocate/release an object at a time, but some applications need to allocate memory for many objects at a time. For example, vector and string are the elements that store them in the continuous memory. Therefore, when the container needs to re-allocate the memory, the memory must be allocated to many elements at a time.
To support this requirement, the C ++ language and the standard library provide two ways to allocate an array of objects at a time: the C ++ language defines the new method of dynamic arrays; the standard library contains a class named allocator.

New and array
Int * pia = new int [get_size ()]; // pia points to the first int

Assign an array to get a pointer of the element type.
Although we usually call the memory allocated by new T [] as a "dynamic array", when we use new to allocate an array, we do not get an array-type object, it is a pointer of the array element type.
Because the allocated memory is not an array type, you cannot call begin or end for dynamic arrays. These functions use array dimensions to return pointers to the first and last elements. For the same reason, the range for statement cannot be used to process elements in a dynamic array.
Initialize an array of dynamically allocated objects
You can initialize the values of the elements in the array by following a pair of empty parentheses after the laughter:

Int * pia = new int [10]; // 10 uninitialized intint * pia2 = new int [10] (); // 10 int values initialized to 0

It is legal to dynamically allocate an empty array.
Although we cannot create a static array object with a size of 0, it is valid to call new [n] WHEN n is equal to 0:

Char arr [0]; // error char * cp = new char [0]; // correct

Release Dynamic Array
To release the dynamic array, we use a special form of delete -- add an empty square bracket pair before the pointer:

Delete p; // p must point to a dynamically allocated object or empty delete [] pa; // pa must point to a dynamically allocated array or empty

Elements in the array are destroyed in reverse order.
Smart pointers and Dynamic Arrays
To use a unique_ptr to manage dynamic arrays, we must follow a pair of empty square brackets behind the object type:

// Up points to an array containing 10 uninitialized int unique_ptr
  
   
Up (new int [10]); up. release (); // automatically use delete [] to destroy its pointer
  

Point to the unique_ptr of the array
Unique_ptr pointing to an array does not support member access operators (Dot and arrow operators)
Other unique_ptr operations are inconvenient

Operation Description
unique_ptr u U can point to a dynamically allocated array. The array element type is T.
unique_ptr u(p) U points to the dynamically allocated array pointed by the built-in pointer p. P must be convertible to type T *
U [I] Returns the object at position I in the array owned by u. u must point to an array.
Allocator class Allocator class

The standard library allocator class is defined in the header file memory, which helps us to separate the memory allocation from the object structure. It provides a type-aware memory allocation method. The memory allocated is raw and unconstructed.
Like vector, allocator is a template. To define an allocator object, we must specify the type of the object that can be allocated by allocator. When an allocator object allocates memory, it determines the proper memory size and alignment position based on the given object type:

Allocator
  
   
Alloc; // you can allocate the allocator object of string auto const p = alloc. allocate (n); // allocate n uninitialized strings.
  

Standard Library allocator class and Its Algorithm

Operation Description
Allocator Defines a allocator object named a, which can allocate memory for objects of type T
A. allocate (n) Allocate an original, constructed memory and save n T-type objects.
A. deallocate (p, n) Release the memory starting from the address in the T * pointer p. This memory stores n T-type objects. p must be a pointer previously returned by allocate, n must be the size required when p is created. Before calling deallocate, you must call destroy for each object created in this memory.
A. construct (p, args) P must be a pointer of the T * type, pointing to a piece of original memory; arg is passed to the T-Type constructor to construct an object in the memory to which p points
A. destroy (p) P is a pointer of the T * type. This algorithm executes the xigou function on the object pointed to by p.

Allocator is allocated as a constructed memory
The memory allocated by allocator is not constructed. We construct objects in this memory as needed.

Auto q = p; // q points to the location after the final constructed element alloc. construct (q ++); // * q is an empty string alloc. construct (q ++, 10, 'C'); // * q is ccccccccccalloc. construct (q ++, "hi"); // * q-bit hi!

To use the memory returned by allocate, we must use construct to construct the object. The memory is constructed and its behavior is undefined.
We can only perform destroy operations on the actually constructed elements.
Copy and fill out uninitialized memory Algorithms
They are all defined in the header file memory.

Allocator Algorithm

These functions create elements at a given destination, instead of allocating memory to them.

Operation Description
Uninitialized_copy (B, e, b2) Copy elements from the input range specified by iterators B and e to the original memory of the constructor b2. The memory to which b2 points must be large enough to accommodate copying of elements in the input sequence.
Uninitialized_copy (B, n, b2) Copy n elements from the element directed by iterator B to the memory starting with b2.
Uninitialized_fill (B, e, t) Create an object in the original memory range specified by iterator B and e. The object value is a copy of t.
Uninitialized_fill_n (B, n, t) Create n objects from the memory address pointed to by iterator B. B must point to a large enough original memory to accommodate a given number of objects.

Related Article

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.