Smart Pointers

Smart pointer technology should address memory leak problems. Smart Pointers are primarily used to manage memory allocated on the heap, encapsulating a normal pointer as a stack object. When the stack object’s lifetime ends, the requested memory is freed in the destructor to prevent memory leaks.

The purpose of smart Pointers is to manage a pointer because ** claims space and forgets to free it at the end of the function, causing a memory leak. ** Using smart Pointers avoids this problem to a large extent, because smart Pointers are classes that, when out of scope of the instance object of the class, automatically call the object’s destructor, which automatically frees resources. So the principle of the smart pointer is to automatically free the memory space at the end of the function, no need to manually free the memory space.

To some extent, smart Pointers are control over memory ownership issues

unique_ptr

Unique_ptr implements the concept of exclusive or strict ownership, ensuring that only one smart pointer can point to the object at a time. Memory leaks caused by forgetting to call DELETE can be avoided.

STD ::unique_ptr has exclusive ownership of the heap memory it holds, that is, the reference count is always 1, and STD :: Unique_ptr releases the heap memory it holds when it is destroyed. Initialize a STD ::unique_ptr object:

// Initialize mode 1
std::unique_ptr<int> sp1(new int(123));
// Initialization mode 2
std::unique_ptr<int> sp2;
sp2.reset(new int(123));
// Initialize mode 3
std::unique_ptr<int> sp3 = std::make_unique<int> (123);
Copy the code

Forbidden replication semantics

STD ::unique_ptr disallows replication semantics

unique_ptr<string> pu1(new string ("hello world")); 
unique_ptr<string> pu2; 
pu2 = pu1;                                      // #1 is not allowed
unique_ptr<string> pu3; 
pu3 = unique_ptr<string>(new string ("You"));   / / # 2 allows
Copy the code

The compilation failed because unique_ptr guarantees unique ownership of the resource, and #2 is allowed because it calls the constructor of unique_ptr, which creates temporary objects that are destroyed after their ownership is transferred to PU3.

To assign one unique_ptr to another, use the library function STD ::move() (equivalent to transferring heap memory held by one STD ::unique_ptr to another).

#include <memory>
int main(a)
{
 std::unique_ptr<int> sp1(std::make_unique<int> (123));
 std::unique_ptr<int> sp2(std::move(sp1));
 std::unique_ptr<int> sp3;
 sp3 = std::move(sp2);
 return 0;
}
Copy the code

Use STD :: Move to transfer the heap memory held by SP1 (value 123) to SP2 and sp2 to SP3. Finally, SP1 and SP2 no longer hold a reference to heap memory and become an empty smart pointer object. Empty smart pointer objects still crash when called, but this syntax emphasizes that you’re transferring ownership and lets you know exactly what you’re doing instead of calling the old pointer.

Use unique_ptr

The life cycle of the unique_ptr pointer itself: from the time the unique_ptr pointer is created until it leaves scope. When it leaves the scope, if it points to an object, the object to which it points is destroyed (the delete operator is used by default, and the user can specify other actions).

The relationship between the unique_ptr pointer and the object to which it points: During the life cycle of the smart pointer, the object to which the smart pointer points can be changed. For example, the smart pointer can be specified by the constructor when it is created, respecified by the reset method, released by the release method, and transferred by moving semantics.

#include <iostream>
#include <memory>

int main(a) {{std::unique_ptr<int> uptr(new int(10));  // Bind dynamic objects
        //std::unique_ptr
      
        uptr2 = uptr; // Cannot be assigned
      
        //std::unique_ptr
      
        uptr2(uptr); // Cannot copy
      
        std::unique_ptr<int> uptr2 = std::move(uptr); // Switch ownership
        uptr2.release(a);// Release ownership
    }
    // Out of scope of upTR, memory freed
}
Copy the code

shared_ptr

Shared_ptr implements the concept of shared ownership. Multiple smart Pointers can point to the same object, and the object and its associated resources are released when “the last reference is destroyed.” Resources can be shared by more than one pointer, and the counting mechanism is used to indicate that the resource is shared by more than one pointer.

Shared_ptr Multiple Pointers to the same object. Shared_ptr uses reference counting, and each copy of shared_ptr points to the same memory. Each time it is used, the internal reference count increases by 1, and each time it is destructed, the internal reference count decreases by 1, and when it reaches 0, the pointed heap memory is automatically deleted. Reference counting inside shared_ptr is thread-safe, but objects are read with locks.

#include <iostream>
#include <memory>
int main(a) {
    {
        std::shared_ptr<int> sh_ptr = std::make_shared<int> (10);
        std::cout << sh_ptr.use_count() << std::endl;
         std::cout << *sh_ptr<< std::endl; // Get the value of the pointer

        std::weak_ptr<int> wp(sh_ptr);
        std::cout << wp.use_count() << std::endl;

        if(! wp.expired()){
            std::shared_ptr<int> sh_ptr2 = wp.lock(a);//get another shared_ptr
            *sh_ptr = 100;
            std::cout << wp.use_count() << std::endl; }}//delete memory
}
Copy the code
  • Use_count Returns the number of reference counts
  • Unique returns exclusive ownership (use_count is 1)
  • Swap swaps two shareD_ptr objects.
  • Reset Relinquishes ownership of an internal object or changes to the owning object cause the reference count of the original object to be reduced
  • Get returns the internal object (pointer), which is the same as using the object directly, since the () method has been overridden
int main(a)
{
	string *s1 = new string("s1");

	shared_ptr<string> ps1(s1);
	shared_ptr<string> ps2;
	ps2 = ps1;

	cout << ps1.use_count()<<endl;	/ / 2
	cout<<ps2.use_count()<<endl;	/ / 2
	cout << ps1.unique()<<endl;	/ / 0

	string *s3 = new string("s3");
	shared_ptr<string> ps3(s3);

	cout << (ps1.get()) << endl;	//033AEB48
	cout << ps3.get() << endl;	//033B2C50
	swap(ps1, ps3);	// Exchange owned objects
	cout << (ps1.get())<<endl;	//033B2C50
	cout << ps3.get() << endl;	//033AEB48

	cout << ps1.use_count()<<endl;	/ / 1
	cout << ps2.use_count() << endl;	/ / 2
	ps2 = ps1;
	cout << ps1.use_count()<<endl;	/ / 2
	cout << ps2.use_count() << endl;	/ / 2
	ps1.reset(a);// Drop ps1 ownership, reduce reference count
	cout << ps1.use_count()<<endl;	/ / 0
	cout << ps2.use_count()<<endl;	/ / 1
}
Copy the code

weak_ptr

Weak_ptr is an intelligent pointer introduced in order to cooperate with SHAREd_PTR. Because it does not have the behavior of ordinary Pointers and does not overload operator* and ->, its biggest role lies in assisting shared_PTR to work and observing the use of resources like a bystander. Weak_ptr can manipulate resources by using a very important member function lock() to obtain an available shared_Ptr object from the observed shared_Ptr.

When two objects point to each other with a shared_ptr member variable, a circular reference is created, invalidating reference counting and resulting in a memory leak. Weak_ptr can be used to solve deadlock problems in shared_Ptr.

Weak_ptr is an intelligent pointer that does not control the life cycle of the object. It points to an object managed by shareD_PTR. The memory management of the object is that strongly referenced shared_PTR. Weak_ptr only provides a means of accessing the managed object.

#include <iostream>
#include <memory>
int main(a)
{
 Create a STD ::shared_ptr object
 std::shared_ptr<int> sp1(new int(123));
 std::cout << "use count: " << sp1.use_count() << std::endl;
 // Use the constructor to get a STD :: Weak_ptr object
 std::weak_ptr<int> sp2(sp1);
 std::cout << "use count: " << sp1.use_count() << std::endl;
 // Get a STD :: Weak_ptr object using the assignment operator
 std::weak_ptr<int> sp3 = sp1;
 std::cout << "use count: " << sp1.use_count() << std::endl;
 // Get another STD :: Weak_ptr from a STD :: Weak_ptr object
 std::weak_ptr<int> sp4 = sp2;
 std::cout << "use count: " << sp1.use_count() << std::endl;
 return 0;
}
Copy the code

No matter how STD :: Weak_ptr is created, it does not increase the reference count of the resource, so the value of the output reference count is 1 each time.

STD :: Weak_ptr does not manage the life cycle of an object, so its referenced object may be destroyed at some point. STD :: Weak_ptr provides a expired() method to do this and returns true, indicating that the referenced resource no longer exists; Return false to indicate that the resource still exists, in which case you can use STD :: Shared_ptr lock() method to obtain a STD ::shared_ptr object and continue to manipulate the resource

//tmpConn_ is a STD :: Weak_ptr 
      
        object
      
// the TcpConnection referenced by tmpConn_ has been destroyed
if (tmpConn_.expired())
 return;
std::shared_ptr<TcpConnection> conn = tmpConn_.lock(a);if (conn)
{
 // perform operations on conn.
}
Copy the code

The classic example of the application scenario of STD :: Weak_PTR is the subscriber mode or observer mode. In the case of subscribers, a consumer publisher will publish messages to a subscriber only if the subscriber exists, rather than managing the subscriber’s life cycle.

class Subscriber
{
};
class SubscribeManager
{
public:
 void publish(a)
 {
     for (const auto& iter : m_subscribers)
     {
         if(! iter.expired())
         {
             //TODO: sends messages to subscribers}}}private:
 std::vector<std::weak_ptr<Subscriber>> m_subscribers;
};
Copy the code