Happy! 1024C++ smart pointer

Blog homepage: @jarvis of the stars and the moon
Welcome to follow: LikeCollectLeave a message
Series of columns:C/C++ column
Please don’t believe that victory is as easy to come by as the dandelions on the hillside, but please believe that there is always some beauty in the world that is worthy of our all-out efforts, even if we are shattered to pieces!
Let’s work together to pursue and become a better version of ourselves!

Article directory

  • Preface
  • 1. Why do we need smart pointers?
  • 2. Memory leak
  • 3. The use and principle of smart pointers
  • Summarize

Tip: The following is the text of this article. The following cases are for reference.

Foreword

I wish all programmers a happy 1024! ! ! This time I would like to share with you some knowledge about smart pointers and some interesting questions about the value transfer cup. I hope you will gain something from reading it!

1. Why do we need smart pointers?

C++ is a language with a long history. C++ has many advantages. It has high execution efficiency and can directly operate memory and pointers. Many of the phenomenal software we see now, such as windows, are written in C++. , but C++ has a big shortcoming that it is easy to leak memory as you write. Even many excellent programmers may make this mistake if they don’t pay attention, so some people have studied intelligence. pointers to solve this problem.

2. Memory leak

2.1 What is a memory leak and the dangers of memory leaks

What is a memory leak: A memory leak refers to a situation where a program fails to release memory that is no longer in use due to negligence or error. Memory leaks do not mean the physical disappearance of memory, but that after the application allocates a certain segment of memory, due to a design error, it loses control of this segment of memory, thus causing a waste of memory.

Hazards of memory leaks: Memory leaks in long-running programs have a great impact, such as operating systems, background services, etc. Memory leaks will cause the response to become slower and slower, and eventually freeze.

Refer to the code below

void TestMemory()
{
    //1. Memory has been applied for and forgotten to be released.
    int *p1 = (int*)malloc(sizeof(int));
    int *p2 = new int;
    
    // 2. Exception security issues
    int* p3 = new int[10];
 
    Func(); // The Func function here throws an exception, causing delete[] p3 not to be executed and p3 not to be released.
 
    delete[] p3;
}

2.2 Classification of memory leaks
We are mainly concerned about two aspects of memory leaks:

  • Heap memory leak (Heap leak)
    Heap memory refers to a portion of the heap allocated through malloc/calloc/realloc/new, etc. as needed during program execution.
    Block memory must be deleted by calling the corresponding free or delete after use. Assume that the design error of the program causes this part
    If the memory is not released, this part of the space will no longer be used, and a Heap Leak will occur.

  • System resource leak
    Refers to the program using resources allocated by the system, such as sockets, file descriptors, pipes, etc., without using corresponding functions to release them.
    If it is dropped, it will lead to a waste of system resources, which may seriously lead to reduced system performance and unstable system execution.

2.3 How to avoid memory leaks

1. Develop good design standards in the early stage of the project, develop good coding standards, and remember to release the memory space that matches the application. ps:
this ideal state. But if you encounter an exception, even if you pay attention to the release, problems may still occur. The next smart pointer is needed to manage it to ensure it.
2. Use RAII ideas or smart pointers to manage resources.
3. Some companies have internal standards for using internally implemented private memory management libraries. This library comes with built-in memory leak detection options.
4. If something goes wrong, use the memory leak tool to detect it. ps: However, many tools are unreliable or expensive.
Summarize:
Memory leaks are very common, and the solutions are divided into two types: 1. Preventive type. Such as smart pointers, etc. 2. Check for errors afterwards. Like leaking
Leak detection tool.

3. The use and principle of smart pointers

3.1 What is RAII
RAII (Resource Acquisition Is Initialization)is a method that uses the object life cycle to control program resources (such as
storage, file handles, network connections, mutexes, etc.).

Acquire resources when the object is constructed, then control access to the resources so that they remain valid throughout the object’s life cycle, Finally
Release resources
when the object is destroyed. With this, we actually delegate the responsibility of managing a resource to an object. Do this
The law has two major benefits:

  1. No need to explicitly release resources.
  2. In this way, the resources required by the object remain valid throughout its lifetime.
//SmartPtr class designed using RAII ideas
template<class T>
class SmartPtr {
public:
    SmartPtr(T* ptr = nullptr)
       : _ptr(ptr)
   {}
    ~SmartPtr()
   {
        if(_ptr)
            delete _ptr;
   }
    
private:
    T* _ptr;
};

3.2 Principle of smart pointers
The above-mentioned SmartPtr cannot be called a smart pointer because it does not yet have the behavior of a pointer. Pointers can be dereferenced or
To access the content in the pointed space through ->, therefore: * and -> must be overloaded in the AutoPtr template class before they can be
Use it like a pointer.

template<class T>
class SmartPtr
{
public:
//RAII Thoughts
SmartPtr(T* ptr)
:_ptr(ptr)
{}

~SmartPtr()
{
cout <<"delete"<<_ptr << endl;
//delete[] _ptr;
delete _ptr;
_ptr = nullptr;
}

//like a pointer
T & operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}

T*Get()
{
return _ptr;
}
private:
T* _ptr;
};

To summarize the principles of smart pointers:

  1. RAII characteristics
  2. Overload operator* and operator-> to behave like pointers.

3.3 std::auto_ptr
std::auto_ptr documentation
The C++98 version of the library provides auto_ptr smart pointers. The use and problems of auto_ptr demonstrated below.
The implementation principle of auto_ptr: the idea of transfer of management rights. The following simplified simulation implements a copy of bit::auto_ptr to understand its origin.
reason

template<class T>
class auto_ptr
{
public:
//RAII Thoughts
auto_ptr(T* ptr)
:_ptr(ptr)
{}

~auto_ptr()
{
if(_ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
}

//sp2(sp1)
auto_ptr(auto_ptr<T> & amp; sp)
:_ptr(sp._ptr)
{
sp._ptr = nullptr;
}

// ...

//like a pointer
T & operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}

T*get()
{
return _ptr;
}
private:
T* _ptr;
};

3.4 std::unique_ptr
A more reliable unique_ptr is available in C++11
unique_ptr document
The implementation principle of unique_ptr: simple and crude copy prevention. The following simplified simulation implements a copy of UniquePtr to understand its origin.
reason

//C++11 library only updated smart pointer implementation
// Before C++11 came out, boost eliminated the more useful scoped_ptr/shared_ptr/weak_ptr
// C++11 absorbs the essence of smart pointers in the boost library
// C++11->unique_ptr/shared_ptr/weak_ptr
// unique_ptr/scoped_ptr
// Principle: Simple and crude -- anti-copy
template<class T>
class unique_ptr
{
public:
//RAII Thoughts
unique_ptr(T* ptr)
:_ptr(ptr)
{}

~unique_ptr()
{
if(_ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
}

//like a pointer
T & operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}

T*get()
{
return _ptr;
}


//private:
// // sp2(sp1)
// // C++98
// // 1. Only declare, not implement
// // 2. Declare it private
// unique_ptr(const unique_ptr<T> & amp; sp);

unique_ptr(const unique_ptr<T> & amp; sp) = delete;
unique_ptr<T> & amp; operator=(const unique_ptr<T> & amp; sp) = delete;

private:
T* _ptr;
};

3.5 std::shared_ptr
C++11 begins to provide a more reliable shared_ptr that supports copying
std::shared_ptr documentation
The principle of shared_ptr is to realize resource sharing between multiple shared_ptr objects through reference counting.

  1. Internally, shared_ptr maintains a count for each resource to record that the resource is shared by several objects.
  2. When the object is destroyed (that is, the destructor is called), it means that the resource is no longer used, and the reference count of the object is decremented by one.
  3. If the reference count is 0, it means that you are the last object to use the resource and must release the resource;
  4. If it is not 0, it means that other objects besides itself are using the resource, and the resource cannot be released, otherwise other objects
    The elephant becomes a wild pointer.
//Reference counting supports multiple copies to manage the same resource, and the last destructor object releases the resource.
template<class T>
class shared_ptr
{
public:
void Release()
{
if (--(*_pCount) == 0 & amp; & amp; _ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;

delete _pCount;
_pCount = nullptr;
}
}

// RAII Thoughts
shared_ptr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}

~shared_ptr()
{
Release();
}

shared_ptr(const shared_ptr<T> & amp; sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
(*_pCount) + + ;
}

\t\t

// sp1 = sp3
shared_ptr<T> & operator=(const shared_ptr<T> & sp)
{
//if (this != & amp;sp)
if (_ptr != sp._ptr)
{
Release();

_ptr = sp._ptr;
_pCount = sp._pCount;
+ + (*_pCount);
}

return *this;
}


//like a pointer
T & operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}

T*get()
{
return _ptr;
}

private:
T* _ptr;
int* _pCount;
};
}

Several questions about shared_ptr:

  1. Are shared_ptr smart pointers thread-safe?
    Yes, the addition and subtraction of reference counts are protected by locking. But pointing to resources is not thread-safe. Thread safety issues pointing to resources on the heap are handled by the person accessing them, and smart pointers do not care about them and cannot control them. The thread safety issue of reference counting is what smart pointers have to deal with
  2. Thread safety issues of shared_ptr (we will leave it to the next section to explain)

Summary

This article introduces several issues related to smart pointers. In the next section, we will talk in detail about the thread safety issues of shared_ptr, as well as knowledge points such as circular references. I hope it will be helpful to everyone!