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;
}
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.
unique_ptr::getReturns a pointer to the managed object or nullptr if no object is owned.
That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.
It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };
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))
{}
...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With