Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return a unique_ptr to polymorphic type

Tags:

c++

c++11

I am trying to be a bit safer by returning a unique_ptr from a few of my methods rather than a raw pointer. However, I am a little confused when it comes to returning a unique pointer to a polymorphic type.

How do we return a unique pointer of base class type that points to a derived class type?

Also, as a secondary question of less importance - Am I creating my derived class from a base class properly using the move constructor?

Here is my minimal example:

// Standard Includes
#include <exception>
#include <memory>
#include <string>
#include <sstream>

//--------------------------------------------------------------------------------------------------
class BaseResult
{
public:

    std::string m_x;

    virtual ~BaseResult() {};
};

class DerivedResult : public BaseResult
{
public:

    int m_y;

    DerivedResult()
        :
        BaseResult()
    {}

    DerivedResult(const DerivedResult & rhs)
        :
        BaseResult(rhs)
      , m_y       (rhs.m_y)
    {}

    DerivedResult(DerivedResult && rhs)
        :
        BaseResult(std::move(rhs))
      , m_y(rhs.m_y)
    {}

    DerivedResult(BaseResult && rhs)
        :
        BaseResult(std::move(rhs))
        , m_y()
    {
    }

    ~DerivedResult() {}
};

class BaseCalc
{
public:

    virtual ~BaseCalc() {}

    virtual std::unique_ptr<BaseResult> Calc() const
    {
        std::unique_ptr<BaseResult> result(new BaseResult);
        result->m_x = "Base Calced";

        return result;
    }

};

class DerivedCalc : public BaseCalc
{
public:

    virtual ~DerivedCalc() {}

    virtual std::unique_ptr<BaseResult> Calc() const
    {
        // I need to rely on the base calculations to get the fields relevant to the base
        std::unique_ptr<BaseResult> baseResult = BaseCalc::Calc();

        // However, I want to perform my addtional calculation relevant to derived, here.
        std::unique_ptr<DerivedResult> result(new DerivedResult(std::move(*baseResult)));
        result->m_y = 2;

        /* Results in error
        return result;
        */
        return std::move(result); // Is this how we do it?
    }
};

//--------------------------------------------------------------------------------------------------
int main()
{
    DerivedCalc calculator;
    std::unique_ptr<BaseResult> temp = calculator.Calc();

    // Cast - Got this part from https://stackoverflow.com/questions/21174593/
    std::unique_ptr<DerivedResult> actualResult;

    if (DerivedResult * cast = dynamic_cast<DerivedResult *>(temp.get()))
    {
        actualResult = std::unique_ptr<DerivedResult>(cast, std::move(temp.get_deleter()));
        temp.release();
    }
    else
    {
        std::exception("Failed to cast to DerivedResult");
    }

    std::string x = actualResult->m_x;
    int y = actualResult->m_y;

    return 0;
}
like image 293
Christopher Pisz Avatar asked Jun 09 '17 16:06

Christopher Pisz


People also ask

Can a function return unique_ptr?

unique_ptr<T> does not allow copy construction, instead it supports move semantics. Yet, I can return a unique_ptr<T> from a function and assign the returned value to a variable. The code above compiles and works as intended.

What does get () do on a unique_ptr?

unique_ptr::getReturns a pointer to the managed object or nullptr if no object is owned.

Does unique_ptr delete automatically?

That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.

Can you assign a unique_ptr?

It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };


1 Answers

Answering your first question, "How do we return a unique pointer of base class type that points to a derived class type?" (note that std::make_unique requires c++14):

class DerivedCalc : public BaseCalc
{
public:

    virtual ~DerivedCalc() {}

    virtual std::unique_ptr<BaseResult> Calc() const
    {
        // I need to rely on the base calculations to get the fields relevant to the base
        std::unique_ptr<BaseResult> baseResult = BaseCalc::Calc();

        // However, I want to perform my addtional calculation relevant to derived, here.
        std::unique_ptr<BaseResult> result(std::make_unique<DerivedResult>(std::move(*baseResult)));
        DerivedResult& derived = static_cast<DerivedResult&>(*result);
        derived.m_y = 2;

        return result;
    }
};

For your 2nd question, "Am I creating my derived class from a base class properly using the move constructor?", your version works, but you can also move the derived data field for optimal performance:

class DerivedResult : public BaseResult
{
    ...
    DerivedResult(DerivedResult && rhs)
        :
        BaseResult(std::move(rhs))
        , m_y(std::move(rhs.m_y))
    {}
    ...
};
like image 57
Jay West Avatar answered Oct 24 '22 07:10

Jay West