1

Have a look at the sample code below and it's output.

I have a simple Base class with just an int* member variable.

I create a unique_ptr from an existing object of Base.

When the pointer and the object goes out of scope, the destructor is called twice, first for the unique_ptr and then for the object.

I thought the unique_ptr would own the object and take care of destroying it??

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

class Base
{
protected:
    int* status;

public:
    // default constuctor
    Base() : Base(0)
    {
    }

    // custom constuctor
    Base(int a){
        this->status = new int(a);
        std::cout << "\n" << "Base:" << *status;
    }

    // destructor
    ~Base(){
        std::cout << "\n" << "Base Destroyed:" << *status;
        delete this->status;
    }

    void info(){
        std::cout << "\n" << "Base status:" << *status;
    }    
};

int main(int argc, const char * argv[])
{
    {
        Base base(1);

        // create from existing object
        std::unique_ptr<Base> uptrBase1 = std::make_unique<Base>(base);

        // create from new
        std::unique_ptr<Base> uptrBase3 = std::make_unique<Base>();

        std::cout<<"\n" << "Ending scope";
    }

    std::cout<<"\n";
    return 0;
}

The output I get is as below:

Base:1
Base:0
Ending scope
Base Destroyed:0
Base Destroyed:1
Base Destroyed:0TestCppProject(4373,0x1000d5dc0) malloc: *** error for object 0x1005ac970: pointer being freed was not allocated
TestCppProject(4373,0x1000d5dc0) malloc: *** set a breakpoint in malloc_error_break to debug
Program ended with exit code: 9
revolutionary
  • 3,314
  • 4
  • 36
  • 53
  • 3
    Nothing to do with `unique_ptr`. You need to obey the "Law of Three" (or "Law of Five"). – Ulrich Eckhardt Feb 01 '20 at 19:03
  • Assuming there is even a point to not having the `status` member be directly of type `int` instead of `int*`, you should make the `status` member `std::unique_ptr`, so that you don't get the rule of three violation. I don't really understand why you are trying to use `std::unique_ptr` in `main` where it is (at least based on your shown example) pointless, yet you don't use it where it makes a difference. – walnut Feb 01 '20 at 19:23
  • @walnut This code has no real purpose, but just to demonstrate the behaviour of unique_ptr – revolutionary Feb 01 '20 at 19:29
  • Add your copy and move operations and watch what they print. This will allow you to visualize the other members that are generated by the compiler. – Martin York Feb 01 '20 at 20:44

1 Answers1

3

I thought the unique_ptr would own the object and take care of destroying it??

You cannot take ownership of automatic storage duration object. Lifetime of such objects controlled by compiler and it ends at the end of block they defined on. Period. What this line does:

std::unique_ptr<Base> uptrBase1 = std::make_unique<Base>(base);

it creates dynamic storage duration object and initializes it with base. Ie logically this code is equal to:

Base *ptr = new Base( base );
std::unique_ptr<Base> uptrBase1( ptr );

so you make a copy of base and give ownership of this newly created object to uptrBase1. As you violated What is The Rule of Three? this leads to UB due to double destroying of internal pointer you created inside class Base and as you created 3 objects of type Base it's destructor called 3 times.

Btw: if you follow the practice and make status std::unique_ptr compiler will not allow you to violate the rule and the line mentioned before would fail to compile.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • After i implemented the move and copy constructors, I see how the copy is taking place for the Base object i defined on the stack. Now it makes sense, for what is going on! I think it was a good exercise to see how unique_ptr deals with objects on the stack – revolutionary Feb 01 '20 at 19:36