C++ smart pointer example details

  • 2020-04-02 02:31:46
  • OfStack

In this paper, the concept and usage of smart pointer in C++ are expounded in detail through examples, which will help readers to deepen their understanding of smart pointer. Details are as follows:

A list,

Since the C++ language has no automatic memory recall mechanism, the programmer manually deletes memory every time new comes out. The programmer forgets to delete, the process is too complex, resulting in no delete, the exception causes the program to exit prematurely, and it is not uncommon for a delete not to be executed.
This kind of problem can be effectively alleviated by using smart pointer. This article mainly explains the use of smart pointer. Include: STD ::auto_ptr, boost::scoped_ptr, boost::shared_ptr, boost::scoped_array, boost::shared_array, boost::weak_ptr, boost::weak_ptr, boost:: usive_ptr. You might wonder, is it really necessary to have so many smart Pointers just to solve the new and delete matching problem? After reading this article, I think you will have the answer in your mind.

The seven smart Pointers (smart_ptr) are explained in the following order.
 
2. Specific use

1, the blanket

To the compiler, the smart pointer is actually a stack object, not a pointer type, and at the end of the stack object's life, the smart pointer releases heap memory it manages through a destructor. All smart Pointers have "operator-" overridden > "Operator to directly return a reference to the object to manipulate the object. The original method of accessing the smart pointer USES the. Operator.

The get() function is used to access the bare pointer contained in the smart pointer. Since the smart pointer is an object, if (my_smart_object) is always true, and to determine whether the naked pointer to the smart pointer is null, you need to say: if (my_smart_object.get()).

The smart pointer contains the reset() method, and if no parameter is passed (or NULL is passed), the smart pointer frees the memory currently managed. If an object is passed, the smart pointer frees the current object to manage the newly passed object.
We write a test class to aid analysis:


class Simple {
 public:
 Simple(int param = 0) {
  number = param;
  std::cout << "Simple: " << number << std::endl; 
 }
 ~Simple() {
  std::cout << "~Simple: " << number << std::endl;
 }
 void PrintSomething() {
  std::cout << "PrintSomething: " << info_extend.c_str() << std::endl;
 }
 std::string info_extend;
 int number;
};

2, STD: : auto_ptr

STD ::auto_ptr belongs to STL, and of course in namespace STD, it contains the header file #include < The memory > Can be used. STD ::auto_ptr makes it easy to manage a single heap memory object.

We start from the code analysis:


void TestAutoPtr() {
std::auto_ptr<Simple> my_memory(new Simple(1));  //Create the object and print: Simple: 1
if (my_memory.get()) {              //Determines if the smart pointer is null
my_memory->PrintSomething();          //Use the operator -> Calls functions in the smart pointer object
my_memory.get()->info_extend = "Addition";   //Use get() to return a bare pointer, and then assign a value to the internal object
my_memory->PrintSomething();          //Print again to indicate that the assignment was successful
(*my_memory).info_extend += " other";      //Use operator* to return the inner object of the smart pointer, and then call the function in the smart pointer object with ".
my_memory->PrintSomething();          //Print again to indicate that the assignment was successful
 }
}                       //My_memory stack object near the end of its life, destructor heap object Simple(1)

The execution result is:


Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
~Simple: 1

The above is the normal use of STD ::auto_ptr code, everything seems to be fine, in any case without us showing the use of the damn delete.
 
It didn't last long. Here's another example:


void TestAutoPtr2() {
 std::auto_ptr<Simple> my_memory(new Simple(1));
 if (my_memory.get()) {
  std::auto_ptr<Simple> my_memory2;  //Create a new my_memory2 object
  my_memory2 = my_memory;       //Copy old my_memory to my_memory2
  my_memory2->PrintSomething();    //Output message, copy successfully
  my_memory->PrintSomething();    //collapse
 }
}

Finally, the above code caused a crash, which is absolutely in line with the C++ programming ideas, actually crashed, after following up the source code of STD ::auto_ptr, we see that the culprit is "my_memory2 = my_memory", this line of code, my_memory2 completely seized the memory management ownership of my_memory, leading to my_memory dangling, the final use of the crash.

Therefore, you should never use the "operator=" operator when using STD ::auto_ptr. As a library, the user is not allowed to use, not explicitly rejected, it will feel a bit unexpected.
 
After looking at the first example of STD ::auto_ptr, let's look at one more:


void TestAutoPtr3() {
 std::auto_ptr<Simple> my_memory(new Simple(1));
 
 if (my_memory.get()) {
  my_memory.release();
 }
}

The execution result is:


Simple: 1

See anything unusual? The object we created was not destructed and did not output "~Simple: 1", resulting in a memory leak. When we don't want my_memory to survive, we call the release() function to free up memory, which results in a memory leak (in memory constrained systems, if my_memory takes up too much memory, we consider giving it back as soon as it's used, rather than waiting for my_memory to end its life).

The correct code should be:


void TestAutoPtr3() {
 std::auto_ptr<Simple> my_memory(new Simple(1));
 if (my_memory.get()) {
  Simple* temp_memory = my_memory.release();
  delete temp_memory;
 }
}

or


void TestAutoPtr3() {
 std::auto_ptr<Simple> my_memory(new Simple(1));
 if (my_memory.get()) {
  my_memory.reset(); //Free the memory managed within my_memory
 }
}

The original STD ::auto_ptr release() function just gives memory ownership, which is clearly not in line with the C++ programming philosophy.
Summary: STD ::auto_ptr can be used to manage pairs of memory for a single object, however, note the following:

(1)       Try not to use "operator=". If so, do not use the previous object.
(2)       Remember that the release() function does not release the object, just return ownership.
(3)       STD ::auto_ptr is best not passed as a parameter (you can write your own code to determine why not).
(4)       Because of the "operator=" problem with STD ::auto_ptr, objects managed by STD ::vector cannot be placed in containers such as STD ::vector.
Using an STD ::auto_ptr has a lot of restrictions and it can't be used to manage an array of heap memory. This should be something you are thinking about right now.
Because STD ::auto_ptr caused a number of problems, some of the designs were not very consistent with the C++ programming philosophy, so the following boost smart pointer was triggered, which can solve the above problem.
Let's keep looking down.
 
3, boost: : scoped_ptr

Boost ::scoped_ptr belongs to the boost library, defined in the namespace boost, and includes the header file #include < Boost/smart_ptr HPP > Can be used. Boost ::scoped_ptr, like STD ::auto_ptr, makes it easy to manage a single heap object. In particular, boost::scoped_ptr's proprietary ownership avoids some of the annoying problems with STD ::auto_ptr.
Let's start with the code:


void TestScopedPtr() {
 boost::scoped_ptr<Simple> my_memory(new Simple(1));
 if (my_memory.get()) {
  my_memory->PrintSomething();
  my_memory.get()->info_extend = "Addition";
  my_memory->PrintSomething();
  (*my_memory).info_extend += " other";
  my_memory->PrintSomething();
  
  my_memory.release();      //Error: scoped_ptr has no release function
  std::auto_ptr<Simple> my_memory2;
  my_memory2 = my_memory;    //Error: scoped_ptr does not override operator= to cause a transfer of ownership
 }
}

First, we can see that boost::scoped_ptr works just as well as auto_ptr. But it does not have a release() function, which did not cause the previous memory leak problem. Second, because boost::scoped_ptr is proprietary, explicitly denying users the ability to write statements like "my_memory2 = my_memory" can alleviate several annoying problems with STD ::auto_ptr.
      Since boost::scoped_ptr has exclusive ownership, when we really need to copy the smart pointer, the requirements are not met, so we introduce a smart pointer to handle replication, parameter passing, and this is boost::shared_ptr.
 
4, boost: : from

Boost ::shared_ptr belongs to the boost library, defined in the namespace boost, and contains the header #include < Boost/smart_ptr HPP > Can be used. We saw above that boost::scoped_ptr has exclusive ownership, no assignment or copying is allowed, and boost::shared_ptr is dedicated to Shared ownership, which USES reference counting internally because of Shared ownership. Boost ::shared_ptr is also used to manage a single heap memory object.
Let's start with the code:


void TestSharedPtr(boost::shared_ptr<Simple> memory) { //Note: no reference (or const reference) is required
 memory->PrintSomething();
 std::cout << "TestSharedPtr UseCount: " << memory.use_count() << std::endl;
}
 
void TestSharedPtr2() {
 boost::shared_ptr<Simple> my_memory(new Simple(1));
 if (my_memory.get()) {
  my_memory->PrintSomething();
  my_memory.get()->info_extend = "Addition";
  my_memory->PrintSomething();
  (*my_memory).info_extend += " other";
  my_memory->PrintSomething();
 }
 
 std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;
 TestSharedPtr(my_memory);
 std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;
 
 //my_memory.release();//  compile  error:  In the same way, shared_ptr  There is no  release  function 
}

The execution result is:


Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
TestSharedPtr2 UseCount: 1
PrintSomething: Addition other
TestSharedPtr UseCount: 2
TestSharedPtr2 UseCount: 1
~Simple: 1

Boost ::shared_ptr is also easy to use. And there's no release() function. Crucially, boost::shared_ptr maintains a reference count internally, which enables replication, parameter passing, and so on. Boost ::shared_ptr provides a function called use_count() that returns a reference count inside boost::shared_ptr. Looking at the results of the execution, we can see that in the TestSharedPtr2 function, the reference count is 1, after passing the argument (a copy is made here), inside the function TestSharedPtr, the reference count is 2, and after TestSharedPtr returns, the reference count is reduced to 1. Boost ::shared_ptr is great when you need to use a Shared object.
At this point, we've looked at smart pointer management of individual objects, and we're going to talk about smart pointer management of arrays.
 
5, boost: : scoped_array

Boost ::scoped_array belongs to the boost library, defined in the namespace boost, and contains the header file #include < Boost/smart_ptr HPP > Can be used.
      Boost ::scoped_array is used to manage dynamic arrays. Like boost::scoped_ptr, it is proprietary.

Let's start with the code:


void TestScopedArray() {
   boost::scoped_array<Simple> my_memory(new Simple[2]); //Initialize using an array of memory
   if (my_memory.get()) {
    my_memory[0].PrintSomething();
    my_memory.get()[0].info_extend = "Addition";
    my_memory[0].PrintSomething();
    (*my_memory)[0].info_extend += " other";      //Compile error, scoped_ptr without overloading the operator*
    my_memory[0].release();               //Ditto, no release
    boost::scoped_array<Simple> my_memory2;
    my_memory2 = my_memory;               //Compile error, ibid., without overloading operator=
   }
  }

Boost ::scoped_array is used in much the same way as boost::scoped_ptr, it doesn't support replication, and dynamic arrays need to be initialized. In addition, boost::scoped_array doesn't overload "operator*," which is fine, since we generally use the get() function to be more explicit.

Next is definitely boost::shared_array, a smart pointer class that handles copying and parameter passing with reference counting.
 
6, boost: : shared_array
Boost ::shared_array is a member of the boost library, defined in the namespace boost, and contains the header file #include < Boost/smart_ptr HPP > Can be used.

Since boost::scoped_array has exclusive ownership, it is clear that in many cases (parameter passing, object assignment, etc.) it does not meet the requirements, so we introduce boost::shared_array. Like boost::shared_ptr, reference counting is used internally.

Let's start with the code:


void TestSharedArray(boost::shared_array<Simple> memory) { //Note: no reference (or const reference) is required
 std::cout << "TestSharedArray UseCount: " << memory.use_count() << std::endl;
}
 
void TestSharedArray2() {
 boost::shared_array<Simple> my_memory(new Simple[2]);
 if (my_memory.get()) {
  my_memory[0].PrintSomething();
  my_memory.get()[0].info_extend = "Addition 00";
  my_memory[0].PrintSomething();
  my_memory[1].PrintSomething();
  my_memory.get()[1].info_extend = "Addition 11";
  my_memory[1].PrintSomething();
  //(*my_memory)[0].info_extend += " other"; //  compile  error . scoped_ptr  There is no overload  operator*
 }
 std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
 TestSharedArray(my_memory);
 std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
}

The execution result is:


Simple: 0
Simple: 0
PrintSomething:
PrintSomething: Addition 00
PrintSomething:
PrintSomething: Addition 11
TestSharedArray2 UseCount: 1
TestSharedArray UseCount: 2
TestSharedArray2 UseCount: 1
~Simple: 0
~Simple: 0

Like boost::shared_ptr, it USES a reference count that can be copied and passed as a parameter.
 
So far, the smart Pointers we've covered are STD ::auto_ptr, boost::scoped_ptr, boost::shared_ptr, boost::scoped_array, and boost::shared_array. These smart Pointers are basically enough for us to use, and these are the only 5 types of code that make up 90% of the code that USES a standard smart pointer. But here are two other smart Pointers that are sure to help, but what?
 
7, boost: : weak_ptr

Boost ::weak_ptr belongs to the boost library, defined in the namespace boost, and contains the header file #include < Boost/smart_ptr HPP > Can be used.
Before we get to boost::weak_ptr, let's review the previous section. It seems that two smart Pointers, boost::scoped_ptr and boost::shared_ptr, can solve all the memory management of a single object.
Answer: yes. First, boost::weak_ptr is specially prepared for boost::shared_ptr. Sometimes, we only care about using objects, not the internal reference count. Boost ::weak_ptr is an Observer object of boost::shared_ptr. The Observer means that boost::weak_ptr only references boost::shared_ptr without changing its reference count. When the observed boost::shared_ptr fails, the corresponding boost::weak_ptr fails.
Let's start with the code:


  void TestWeakPtr() {
   boost::weak_ptr<Simple> my_memory_weak;
   boost::shared_ptr<Simple> my_memory(new Simple(1));
 
   std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;
   my_memory_weak = my_memory;
   std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;
}

      The execution result is:


Simple: 1
TestWeakPtr boost::shared_ptr UseCount: 1
TestWeakPtr boost::shared_ptr UseCount: 1
~Simple: 1

      We saw that the internal reference count didn't change much despite the assignment, but of course the reader could try passing arguments and so on.
      Now the question is, what exactly does boost::weak_ptr do? Doesn't seem to have any effect, as shown in the example above, actually boost: : weak_ptr mainly used in the design of software architecture, can be in the base class (the base class is not an abstract base class, but to inherit from the abstract base class virtual base class defined in a boost: : weak_ptr, used to point to a subclass of boost: : from that base class only observe yourself boost: : you know weak_ptr for null subclasses don't have to their assignment, Without affecting the reference count of the subclass boost::shared_ptr to reduce complexity and better manage objects.
 
8, boost: : intrusive_ptr

Boost ::intrusive_ptr belongs to the boost library, which is defined in the namespace boost and contains the header #include < Boost/smart_ptr HPP > Can be used.
After the above six kinds of smart Pointers, for the general program C++ heap memory management is enough, now there is a boost::intrusive_ptr, this is a plug-in type of smart pointer, internal does not contain a reference count, the need to add their own reference count, otherwise compiled. Personally, this smart pointer is not very useful, at least I have not used it. Interested friends to study the source code.
 
 
Third, summary

With all the smart Pointers mentioned above, it is necessary to summarize these smart Pointers:

1. Refuse to use STD ::auto_ptr where you can use the boost library because it is not only incompatible with the C++ programming philosophy, but also extremely error prone.
2. Use boost::scoped_ptr (dynamic arrays, of course, use boost::scoped_array) when you're sure the objects don't need to be Shared.
3. Use boost::shared_ptr (dynamic arrays, of course, use boost::shared_array) when objects need to be Shared.
4. Use boost::weak_ptr when you need to access the boost::shared_ptr object, but you don't want to change its reference count.
5. Last but not least, do not include the delete keyword (or the free function in C) in your code, because it can be managed with smart Pointers.


Related articles: