C++. Development of a class that implements a smart pointer

C++. Development of a class that implements a “smart” pointer

This topic provides an example of developing a template class that implements a smart pointer.


Contents


Search other resources:

1. The concept of a “smart” pointer. Features of implementation

The term “smart” pointer means a class that imitates the work of a regular pointer and extends the functionality of this pointer by introducing additional features. Examples of additional features include:

  • checks for correct pointer usage limits;
  • counting the number of copies of the pointer;
  • freeing the memory pointed to by the pointer, etc.

Using a “smart” pointer makes it impossible to try to free the same piece of memory twice. There are times when two or more different pointers point to the same memory location. After freeing this memory in one of the pointers, an attempt to free it through other pointers will generate an exception and the program will crash. If a specially designed “smart” pointer class is used in the program, this situation will be handled correctly.

From the point of view of the implementation in the class, the following internal data (fields) are defined for the “smart” pointer:

  • a pointer to an object of the given type. If it is a generic class, then a pointer to an object of the generic type T;
  • counter of the number of calls to the object.

To provide minimal smart pointer capabilities in a class, you must implement at least one operator->() operator function that provides access by pointer.

 

2. Smart pointer template class example

The code for the two classes is shown below:

  • class Int that implements some integer. This class is designed for demonstration purposes. Any other class can be used if desired;
  • template class SmartPtr that implements a “smart” pointer.

 

#include <iostream>
using namespace std;

// Topic: smart pointers
// A class
class Int
{
private:
  int d;

public:
  // Constructor
  Int(int _d) : d(_d) { }

  // Access methods
  int Get() { return d; }
  void Set(int _d)
  {
    d = _d;
  }

  // Method Print()
  void Print(string msg)
  {
    cout << msg.c_str() << d << endl;
  }
};

// Class of smart pointer to generic type T
template <typename T>
class SmartPtr
{
private:
  T* p; // pointer to generic type T
  int count; // number of copies

public:
  // Constructor
  SmartPtr(T* _p = nullptr)
  {
    // Write 0 - no copies
    count = 0;
    p = _p;
  }

  // Copy constructor
  SmartPtr(const SmartPtr& obj)
  {
    // A copy is created
    p = obj.p;

    // Increase count
    count++;
  }

  // Copy operator
  SmartPtr operator=(const SmartPtr& obj)
  {
    // A copy is created
    p = obj.p;
    count++;
    return *this;
  }

  // Destructor - destroys the original object,
  // copy objects are not destroyed.
  ~SmartPtr()
  {
    // If there is an object and there are no copies,
    // then we simply destroy this object.
    if ((p != nullptr) && (count == 0))
    {
      cout << "Delete object" << endl;
      delete[] p;
    }
    else
    {
      // otherwise, the copy is simply destroyed
      cout << "Delete copy" << endl;
      count--; // decrease copy count
    }
  }

  // Override operator -> access by pointer
  T* operator->()
  {
    return p;
  }
};

void main()
{
  // 1. Create an object of class Int
  Int* obj1 = new Int(10);
  obj1->Print("obj1: ");

  // 2. Initialize a smart pointer with this object
  SmartPtr<Int> ptr(obj1);
  ptr->Print("ptr->obj: ");

  // 3. Create a copy of a smart pointer
  SmartPtr<Int> ptr2 = ptr; // call the copy constructor
  ptr2->Print("ptr2->obj: ");

  // 4. Create another copy of the smart pointer
  SmartPtr<Int> ptr3;
  ptr3 = ptr2;   // copy operator is called
  ptr3->Print("ptr3->obj: ");
}

After running the program, it will give the following result

obj1: 10
ptr->obj: 10
ptr2->obj: 10
ptr3->obj: 10

 

3. Program code analysis

The template class SmartPtr<T> operates on the generic type T. The class declares a constructor that takes a pointer to type T. When the first instance is created, the internal variable count (number of copies) is set to 0. Incrementing this variable is possible in cases where a copy of the pointer is being created. A copy can be created when a copy constructor or copy operator is called. Thus, the number of smart pointer copies created is tracked.

The most important element of a class is the destructor.

~SmartPtr()
{
  // If there is an object and no copies, then we simply destroy this object.
  if ((p != nullptr) && (count == 0))
  {
    delete[] p;
  }
  else
  {
    // otherwise, just destroy the copy
    count--; // decrease copy count
  }
}

As you can see from the destructor code, the memory for the pointer is freed only once, no matter how many copies it has. In fact, the memory for the pointer for which memory was allocated for the first time is released. If a copy is considered, then no memory is deallocated, only the copy counter is decremented. The destructor ensures that the memory is deallocated correctly in case there are copies of the smart pointer.

 


Related topics