0

I have a class hierarchy as follows

#include <iostream>
#include <memory>

class A
{
public:
    virtual void Print() = 0;
};

class B : public A
{
public:
    void Print()
    {
        std::cout<<"Hello, world\n";
    }
};

void Printer(std::unique_ptr<A> aPtr)
{
    aPtr->Print();
}

int main()
{
    std::unique_ptr<B> bPtr = std::make_unique<B>();
    Printer(std::unique_ptr<A>(bPtr.get()));    // Causing double deletion
    
}

Had I not been working with unique_ptrs, the solution was a no-brainer. The cast from B* to A* during function call would have taken place implicitly and the appropriate method would have been called in the Printer function using dynamic dispatch.

Although, as I'm using unique_ptrs, the compiler is complaining that an argument of type std::unique_ptr<A> can not be initialized by an argument of type std::unique_ptr<B>.

As in the mentioned code, when I try to get the raw pointer from std::unique_ptr<B> object bPtr and wrap it in an std::unique_ptr<A>, I get double deletion problem as the memory occupied by bPtr's object is freed first time when the Printer() function returns, and another time when the main() function returns.

Is there any way around this, so that I get proper casting from std::unique_ptr<B> to std::unique_ptr<A>?

Aayush Anand
  • 25
  • 1
  • 6

2 Answers2

3

First of all, void Printer(std::unique_ptr<A> aPtr) is a very unusual signature for a function that prints a value. Taking a unique pointer by value implies that the function wants to take over ownership, but that makes no sense for a function that only wants to print something.

Remember that a unique pointer is about unique ownership. There can only be one unique pointer managing the life time of one object. Hence you cannot copy a unique pointer.

Raw pointers are ok when no ownership transfer is desired:

void Printer(A* aPtr)
{
    aPtr->Print();
}

Printer(bPtr.get()); 

Or simply pass by const reference as you would if the object is not managed by a unique pointer. void Printer should not need to care whether the object is allocated dynamically or whether it is managed by a unique ptr.

On the other hand, if the function really does take over ownership and does take the unique pointer by value, then you must explicitly move from the original pointer:

void Printer(std::unique_ptr<A> aPtr);

std::unique_ptr<B> bPtr = std::make_unique<B>();
Printer(std::move(bPtr));  

PS: note that the constructor of std::unique_ptr that accepts a raw pointer is explicit to prevent accidental transfer of ownership. You had to explicitly call the constructor, otherwise it wouldn't compile, which is good.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

unique_ptr is about single ownership. I don't think that the goal of your printer function is to do that. I think that you merely can fix your code this way:

void Printer(A* aPtr) { aPtr->Print(); }

int main() {
    std::unique_ptr<B> bPtr = std::make_unique<B>();
    Printer(bPtr.get());  // no double deletion
}

Live Demo

j6t
  • 9,150
  • 1
  • 15
  • 35
Oersted
  • 769
  • 16