Smart Pointer

The c++ STL incluce smart pointer, whick is used to make sure the program’s memory and resource safe.

Smart pointer definded in std header file’s “<” namespance.

Example:


// Use raw pointer
void UseRawPointer() {
  Song* pSoing = new Song(L"Nothing on You", L"Bruno Mars");

  delete pSong;
}

// Smart pointer
void UseSmartPointer() {
  unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));

  wstring s = song2 -> duration;

}

Main step of execute smart pointer

  1. Declare a smart pointer to a auto (local) variable. (Do not use new or malloc expr)

  2. Write a type into the type param.

  3. In the smart pointer constructor, the raw pointer is passed to the new object.

  4. Use overwrite -> and * operaters to assess object.

  5. Allow smart pointer to delete object.

1. unique_ptr

unique_ptr doesn’t share its pointer. it cannot copy to another unique_ptr, and it cannot pass to function through value.

The unique_ptr only can be moved, which means memory resource overship will move to another unique_ptr, and the source of unique_ptr will lost this pointer resource.

We can use move for moving unique_ptr

Example:


unique_ptr<Song> SongFactory(const std::wstring& artist, const std::wstring& title) {
  return make_unique<Song>(artist, title);
}

void MakeSongs() {
  auto song = make_unique<Song>(L"Mr. Children", L"Namonaki Uta");

  vector<wstring> titles = { song -> title };

  unique_ptr<Song> song2 = std::move(song);

  ato song3 = SongFactory(L"Michael Jackson", L"Beat It");

}

2. shared_ptr

shared_ptr can be copied, pass to function parameter through value, and then assign to another shared_ptr instance.

All of the instance are fordward to one object and share each "control block"s access permission.

When reference counting is 0, the control block will delete memory resource and itself.

Example:


auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");

shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));

shared_ptr<Song> sp5(nullptr);

sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");


//Initialize with copy constructor. Increments ref count.
auto sp3(sp2);

//Initialize via assignment. Increments ref count.
auto sp4 = sp2;

//Initialize with nullptr. sp7 is empty.
shared_ptr<Song> sp7(nullptr);

// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.
sp1.swap(sp2);

3. weak_ptr

Sometimes, an object must store a way to access the underlying object of a shared_ptr without incrementing the reference count. This typically occurs when there are circular references between shared_ptr instances.

Best design practice is to avoid shared ownership of pointers whenever possible. However, if shared ownership of shared_ptr instances is unavoidable, or even preferable for some reason, avoid circular references between them. If circular references are unavoidable, or even preferable for some reason, use a weak_ptr to provide one or more owners with weak references to another shared_ptr owner. By using a weak_ptr, you can create a shared_ptr linked to an existing set of related instances, provided the underlying memory resource is still valid. The weak_ptr itself does not participate in reference counting, so it cannot prevent the reference count from becoming zero. However, you can use a weak_ptr to attempt to acquire a new copy of the shared_ptr that initialized that copy. If the memory has been deleted, the bool operator of the weak_ptr returns false. If the memory is still valid, the new shared pointer increments the reference count, and the memory is guaranteed to be valid as long as the shared_ptr variable remains in scope.

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class Controller
{
public:
   int Num;
   wstring Status;
   vector<weak_ptr<Controller>> others;
   explicit Controller(int i) : Num(i), Status(L"On")
   {
      wcout << L"Creating Controller" << Num << endl;
   }

   ~Controller()
   {
      wcout << L"Destroying Controller" << Num << endl;
   }

   // Demonstrates how to test whether the
   // pointed-to memory still exists or not.
   void CheckStatuses() const
   {
      for_each(others.begin(), others.end(), [](weak_ptr<Controller> wp) {
         auto p = wp.lock();
         if (p)
         {
            wcout << L"Status of " << p->Num << " = " << p->Status << endl;
         }
         else
         {
            wcout << L"Null object" << endl;
         }
      });
   }
};

void RunTest()
{
   vector<shared_ptr<Controller>> v{
       make_shared<Controller>(0),
       make_shared<Controller>(1),
       make_shared<Controller>(2),
       make_shared<Controller>(3),
       make_shared<Controller>(4),
   };

   // Each controller depends on all others not being deleted.
   // Give each controller a pointer to all the others.
   for (int i = 0; i < v.size(); ++i)
   {
      for_each(v.begin(), v.end(), [&v, i](shared_ptr<Controller> p) {
         if (p->Num != i)
         {
            v[i]->others.push_back(weak_ptr<Controller>(p));
            wcout << L"push_back to v[" << i << "]: " << p->Num << endl;
         }
      });
   }

   for_each(v.begin(), v.end(), [](shared_ptr<Controller> &p) {
      wcout << L"use_count = " << p.use_count() << endl;
      p->CheckStatuses();
   });
}

int main()
{
   RunTest();
   wcout << L"Press any key" << endl;
   char ch;
   cin.getline(&ch, 1);
}

4. CComPtr and CComQIPtr

https://learn.microsoft.com/zh-cn/cpp/cpp/how-to-create-and-use-ccomptr-and-ccomqiptr-instances?view=msvc-170