Analysis of the use of std::auto_ptr in c++

Preface

Since the C++ language does not have an automatic memory recovery mechanism, the programmer must manually delete every new memory. The programmer forgets to delete, the process is too complicated, and finally there is no delete, the exception causes the program to exit prematurely, and it is not uncommon that the delete is not executed.

Using smart pointers can effectively alleviate such problems. This article mainly explains std::auto_ptr smart pointers.

For the compiler, the smart pointer is actually a stack object, not a pointer type. When the life of the stack object is about to end, the smart pointer releases the heap memory it manages through the destructor. All smart pointers have overloaded the "operator->" operator, which directly returns the reference of the object to manipulate the object. The original method of accessing smart pointers uses the "." operator.

To access the raw pointer contained in the smart pointer, you can use the get() function. Since the smart pointer is an object, if (my_smart_object) is always true. To determine whether the raw pointer of the smart pointer is empty, you need to judge like this: if (my_smart_object.get()).

The smart pointer contains the reset() method. If no parameters are passed (or NULL is passed), the smart pointer will release the currently managed memory. If an object is passed, the smart pointer will release the current object to manage the newly passed object.

Test 1

#include <iostream>
#include <memory>

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;

};

void TestAutoPtr() {

std::auto_ptr<Simple> my_memory(new Simple(1));   // 创建对象,输出:Simple:1

if (my_memory.get()) {                            // 判断智能指针是否为空

my_memory->PrintSomething();                    // 使用 operator-> 调用智能指针对象中的函数

my_memory.get()->info_extend = "Addition";      // 使用 get() 返回裸指针,然后给内部对象赋值

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

(*my_memory).info_extend += " other";           // 使用 operator* 返回智能指针内部对象,然后用“.”调用智能指针对象中的函数

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

  }

}




int main ()
{

	TestAutoPtr(); 
	return 0;
}

Compile and execute, the program executes normally
Insert picture description here

Test 2

std::auto_ptr belongs to STL, of course, in the namespace std, including the header file #include can be used. std::auto_ptr can easily manage a single heap memory object.

#include <iostream>
#include <memory>

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;

};


/*
void TestAutoPtr() {

std::auto_ptr<Simple> my_memory(new Simple(1));   // 创建对象,输出:Simple:1

if (my_memory.get()) {                            // 判断智能指针是否为空

my_memory->PrintSomething();                    // 使用 operator-> 调用智能指针对象中的函数

my_memory.get()->info_extend = "Addition";      // 使用 get() 返回裸指针,然后给内部对象赋值

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

(*my_memory).info_extend += " other";           // 使用 operator* 返回智能指针内部对象,然后用“.”调用智能指针对象中的函数

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

  }

}

*/

void TestAutoPtr2() {

  std::auto_ptr<Simple> my_memory(new Simple(1));

  if (my_memory.get()) {

    std::auto_ptr<Simple> my_memory2;   // 创建一个新的 my_memory2 对象

    my_memory2 = my_memory;             // 复制旧的 my_memory 给 my_memory2

    my_memory2->PrintSomething();       // 输出信息,复制成功

    my_memory->PrintSomething();        // 崩溃

  }

}


int main ()
{


	TestAutoPtr2(); 
	return 0;
}

Compile and use GDB to debug and view.
std::auto_ptr my_memory(new Simple(1)); When executed:
Insert picture description here
Std::auto_ptr is analyzed when executed:

/**
* 构造函数,将auto_ptr绑定到指针__p。
* __p是一个指向new出来的对象的指针,默认为0(NULL)是说auto_ptr的构造函数可以不传参构造,
* 这时成员_M_ptr=0,如果接着解引用auto_ptr对象,将Segmentation fault。当然,通常应用auto_ptr的构造
* 函数会传参的。auto_ptr提供了get函数来判断_M_ptr是否为空、reset函数重置_M_ptr指针。
* 在继承情况下,_M_ptr可以是__p的基类型。
* 构造函数声明为explicit表示禁止参数的自动类型转换(因为它们总是邪恶的)。
*
*/

      explicit auto_ptr(element_type* __p = 0) throw () :

            _M_ptr(__p) {

      }

my_memory.get(); when executed:
Insert picture description here
get() function call analysis:

/**
* 返回auto_ptr管理的指针,这通常用于判断指针是否为空的情况,所以,如果要判断
* auto_ptr管理的指针是否为空,不要使用if(auto_ptr_obj){}而是使用get函数(实际上,
* 因为auto_ptr并没用定义指向element_type的dumb指针的隐式类型转换操作符,所以根本
* 编译不过if(auto_ptr_obj))。
* 但是,auto_ptr并没有禁止你进一步操作你得到的指针,甚至delete它使
* auto_ptr对象内置的指针悬空。
*/

      element_type* get() const throw () {

            return _M_ptr;

      }

my_memory2 = my_memory; When executed:
Insert picture description here
you can see that after the sentence my_memory2 = my_memory is executed, the value of my_memory is passed to my_memory2, and my_memory is cleared, giving up memory management rights, and my_memory2 completely seizes the memory management ownership of my_memory.
Subsequent calls to my_memory->PrintSomething(); made a null pointer reference, and the program reported an error: Segmentation fault (core dumped)
Insert picture description here
eventually caused a crash with the above code. The above code absolutely conforms to the C++ programming idea, and it crashed. Follow up with std: After the source code of :auto_ptr, we see that the culprit is "my_memory2 = my_memory". In this line of code, my_memory2 completely seizes the ownership of my_memory's memory management, causing my_memory to be left empty, and it causes a crash when it is finally used.

Therefore, when using std::auto_ptr, the "operator=" operator must not be used. As a library, users are not allowed to use it, and indeed there is no explicit rejection [1], which is somewhat unexpected.

Test 3

#include <iostream>
#include <memory>

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;

};


void TestAutoPtr3() {

  std::auto_ptr<Simple> my_memory(new Simple(1));

  if (my_memory.get()) {

    my_memory.release();

  }

}
int main ()
{
	TestAutoPtr3(); 
	return 0;
}

Compile and debug,
Insert picture description here
Insert picture description here
you can see my_memory.release(); when executing this sentence, first set the pointer to 0, and then delete this execution. The actually created object is not released, that is, the object is not destroyed, and there is no output "~Simple : 1". This causes a waste of memory.
Although the execution exited normally this time, if it was executed multiple times, the memory might be corrupted.
Insert picture description here
If the program does not actually release the memory through the delete and free statements, it will either crash (execute the release statement before the crash) or exit normally (that is, no release statement is written in the program, but the program exits normally). Then the unreclaimed memory will be reclaimed by the system every time you shut down.
This is a fatal bug: If the program continues to run for many times, it may crash due to memory overflow in one of the following, and the cause may not be found at all (because the first few times are correct).
When we don’t want my_memory to survive, we call the release() function to release the memory, but the result is a memory leak (in a memory-constrained system, if my_memory occupies too much memory, we will consider returning it immediately after use. It does not wait until my_memory expires before returning).

Test 4

#include <iostream>
#include <memory>

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;

};


void TestAutoPtr3() {

  std::auto_ptr<Simple> my_memory(new Simple(1));

  if (my_memory.get()) {

    Simple* temp_memory = my_memory.release();

    delete temp_memory;

  }

}

void TestAutoPtr4() {

  std::auto_ptr<Simple> my_memory(new Simple(1));

  if (my_memory.get()) {

    my_memory.reset();  // 释放 my_memory 内部管理的内存

  }

}


int main ()
{
	TestAutoPtr3(); 
	TestAutoPtr4(); 	
	return 0;
}

Insert picture description here
delete temp_memory; When executed, the destructor is called to release the memory space of the object.

Insert picture description here
my_memory.reset(); When executed, the destructor is called to release the memory space of the object.

to sum up

It turns out that the release() function of std::auto_ptr just gives up memory ownership, which obviously does not conform to C++ programming ideas.

Summary: std::auto_ptr can be used to manage the memory of a single object, but please note the following points:

(1) Try not to use "operator=". If you use it, don't use the previous object.

(2) Remember that the release() function does not release the object, only returns the ownership.

(3) std::auto_ptr is best not passed as a parameter (readers can write code to determine why not).

(4) Due to the "operator=" problem of std::auto_ptr, objects managed by it cannot be placed in containers such as std::vector.

(5) ……

There are so many restrictions on using a std::auto_ptr, and it cannot be used to manage heap memory arrays. This should be what you are thinking about right now. I also think there are a lot of restrictions. One day one is not careful, it will cause problems. .

Guess you like

Origin blog.csdn.net/u014470361/article/details/101643896