Search code examples
c++c++11shared-ptrsmart-pointersunique-ptr

Smart pointers to an object explicitly created object


I have read a lot of issues created in regard to this but was not able to answer my question.

I have created a class as follows -

class exampleClass{
public: 
    exampleClass(int n){
        cout<<"Created Class"<<endl;
        this->number = n;
    }

    ~exampleClass(){
        cout<<endl<<"This class is destroyed Now"<<endl;
    }

    template<typename t> t 
    addNum(t a, t b){
        return a + b;
    }

    void print(){
        cout<<this->number<<endl;
    }
private:
    int number;
};

and I make 2 shared_ptr(or for that matter unique_ptr, error is same) as follows -

int main(){
   exampleClass* object = new exampleClass(60);

   std::shared_ptr<exampleClass> p1(object);
   std::shared_ptr<exampleClass> p2 (object);
   p1->print();
}

Now the error it throws at the end is -

free(): double free detected in tcache 2
Aborted (core dumped)

I am not able to understand why the error at the end. Shouldn't the above code be equal to p2 =p1(in case of shared_ptr) or p2 = std::move(p1) for unique_ptr as both the pointers are for the same object?

TIA
PS - The title might be a little misleading or not accurate,but I did not know what exactly should be a title.


Solution

  • When you create a shared_ptr from a raw pointer, it takes ownership of the raw pointer, and when the smart pointer goes out of scope, it will call delete on the owned resource. Giving the same raw pointer to 2 different shared_ptrs causes a double free, as both of them will try to free the resource.

    If you need 2 shared_ptrs that share the same resource, you can copy the first one:

    int main(){
       exampleClass* object = new exampleClass(60);
    
       std::shared_ptr<exampleClass> p1(object);
       std::shared_ptr<exampleClass> p2 = p1;
    }
    

    This way they share ownership of the resource (sharaed_ptrs have an internal reference counter that tracks how many of them own a resource. When you copy a shared_ptr, the reference counter is incremented. When one goes out of scope, the reference counter is decremented. Only if the counter reaches zero is the resource freed) thus it will only be freed once, when the last shared_ptr owning the resource goes out of scope.

    It's usually preferable to avoid explicitly writing out new and use make_shared, which does the allocation, creates the object for you and returns a shared_ptr that owns it:

    auto object = std::make_shared<exampleClass>(60);
    

    Some additional advanced reading in the topic, not strictly related to the question:

    • performance differences when using make_shared vs manually calling new: here
    • memory implications of using make_shared with weak_ptrs and large objects: here (Thanks for bringig this up @Yakk - Adam Nevraumont, this was new for me :))