What does unique_ptr<T>::operator= do in terms of deallocation

Viewed 81

I'm having troubles understanding fully the assignment operator for unique_ptr. I understand that we can only move them, due to the fact that copy constructor and assignment operators are deleted, but what if a unique_ptr which contains already an allocation is overwritten by a move operation? Is the content previously stored in the smart pointer free'd?

#include <iostream>
#include <memory>

class A{
public:
    A() = default;
    virtual void act() const {
        std::cout << "act from A" << std::endl;
    }
    virtual ~A() {
        std::cout << "destroyed A" << std::endl;
    }
};
class B : public A {
public:
    B() : A{} {}
    void act() const override {
        std::cout << "act from B" << std::endl;
    }

    ~B() override {
        std::cout << "destroyed from B " << std::endl;
    }
};
int main() {
    auto pP{std::make_unique<A>()};
    pP->act();

    ==================== ! =======================
    pP = std::make_unique<B>(); // || std::move(std::make_unique<B>())
    ==================== ! =======================

    pP->act();
    return 0;
}

When I do

pP = std::make_unique<B>();

does it mean that what was allocated in the first lines for pP (new A()) is destructed automatically? Or should I opt for:

pP.reset();
pP = std::make_unique<B>();
3 Answers

Yes, see section 20.9.1, paragraph 4 of the C++11 draft standard

Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:

  • u2.p is equal to the pre-transfer u.p,
  • u.p is equal to nullptr, and
  • if the pre-transfer u.d maintained state, such state has been transferred to u2.d.

As in the case of a reset, u2 must properly dispose of its pre-transfer owned object via the pre-transfer associated deleter before the ownership transfer is considered complete

In other words, it's cleaning up after itself upon assignment like you'd expect.

Yes, replacing the content of a smart pointer will release the previously-held resource. You do not need to call reset() explicitly (nor would anyone expect you to).

Just for the sake of this particular example. It seems polymorphism in your example didn't allow you to draw clear conclusions from output:

act from A
destroyed A
act from B
destroyed from B 
destroyed A

So let's simplify your example and make it straight to the point:

#include <iostream>
#include <memory>

struct A {
    explicit A(int id): id_(id)
    {}

    ~A()
    {
        std::cout << "destroyed " << id_ << std::endl;
    }

    int id_;
};

int main() {
    std::unique_ptr<A> pP{std::make_unique<A>(1)};
    pP = std::make_unique<A>(2);
}

which outputs:

destroyed 1
destroyed 2

Online

I hope this leaves no room for misinterpretation.

Related