Pointers

Raw Pointers


// As defined in C
int *p = new int();         //p is raw pointer.
        

Smart pointers

Automatically deletes the allocated memory when goes out of scope.
How? Allocate memory in ctr, Delete memory in dtr. Bcoz Destructors are automatically executed when Object goes out of scope.
Why? Programmer may allocate memory using new() & later might forget to use delete().
Smart Pointer Implementation Example

template <class T>
class A{                    //This class is Smart pointer
    T *a;         //int *a;
public:
    A(T *r) : a(r) { cout << "Allocated\n"; }              //A(int *r):a(r)
    ~A(){
        delete a;
        cout << "Freed\n";
    }
    T &operator *(){        //int &operator *()    //& returns a only, does not create new copy
        return *a;
    }
};

int main(){
    //Automatic template type deduction => Provided in C++17
    A <int> obj(new int());	//obj is my pointer now. I can carry all pointer operations
    *obj = 10;
    cout << *obj;

    A  <float> obj1(new float());
}

# g++ smart_pointers
Allocated
10
Allocated 

Freed
Freed
        

Unique vs Smart pointer

Unique Shared
What Only 1 Owner of memory, ie only 1 pointer to an object Multiple owners of a resource
Copy Constructible No, any attempt to make a copy of a unique_ptr will cause a compile-time error. copy ctr, assignment operator are deleted Yes
Move Constructible Yes,can be moved using the new move semantics Yes

Unique pointer (type of smart pointer)

Copy Constructible(No)

There can be only one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error.

unique_ptr<int> p(new int(5))       // Okay
unique_ptr<T> c = p;              // Error: Can't copy unique_ptr
            

Move Constructible(Yes)


unique_ptr<int> p(new int(5))            // Okay
unique_ptr<T> cptr = std::move(ptr);     // Okay, resource now stored in cptr
            

Creating unique_ptr using make_unique


make_unique <T>()
    - Avoid use of new operator
    - Provides exception safety, as new and delete internally handled by compiler. 
    - Memory is allocated to 0.

    struct Config {
        int a;
        Config() {
            a = g;
        }
    };
    int main() {
        shared_ptr <Config> ptr = make_unique (); //Call default constructor of class Config
        cout << ptr->a;                                   //15
    }
        

Shared pointer (type of smart pointer)

Copy Constructible(Yes)

Multiple pointers can point to same resource, Owner count is maintained using reference counting When none is referencing the memory, it's deleted automatically.

shared_ptr <int> p   =   int* p   //We can think like this

shared_ptr <T> ptr(new int(2));       // Okay
shared_ptr <T> cptr = ptr;       // Okay.  Are Copy Constructible
        

Copy Initialization(No)

When we initialise with =, we invoke copy-initialisation
Why CI not allowed? Because shared_ptr constructor is explicit and a explicit construtor is not copy-assignable

int* b = new int;
shared_ptr <int> a = b;      //Error
        

Create shared pointer using make_shared


#include <memory>
#include <iostream>
using namespace std;
constexpr int g = 15;
struct Config {
    int a;
    Config() {
        a = g;
    }
};
int main() {
    shared_ptr <Config> ptr = make_shared <Config>(); //Call default constructor of class Config
    cout << ptr->a;                                   //15
}
        
How shared_ptr is implemented internally?
shared_ptr maintains 2 things.
Creation of shared_ptr: reference_count=1
On every new shared_ptr creation: reference_count++
Deletion/Reset: reference_count--

int reference_count;
T* data;                  //and a pointer to the object being shared

template <typename T>
class shared_ptr {
    struct ControlBlock {   //stores a pointer to the object being shared (T* ptr_) and a reference count (int ref_count_).
        T* ptr_;
        int ref_count_;
    };    
    ControlBlock* data_;
public:
    shared_ptr(T* ptr) {
        data_ = new ControlBlock;
        data_->ptr_ = ptr;
        data_->ref_count_ = 1;
    }
    
    shared_ptr(const shared_ptr& other) {
        data_ = other.data_;
        data_->ref_count_++;
    }
    
    ~shared_ptr() {
        if (--data_->ref_count_ == 0) {
            delete data_->ptr_;
            delete data_;
        }
    }    
};