I have a few classes which look like that:
struct equation {};
struct number: equation {
number(int n): value(n) {}
private:
int value;
};
struct operation: equation {
operation(const equation* left, const equation* right)
: left(left), right(right) {}
private:
std::unique_ptr<equation> left, right;
};
They are designed in a way that operation
takes ownership on the pointers that are passed into constructor.
My question is how can I modify this class to be able to safely use it in a next manner:
operation op(new number(123), new number(456));
It seems to me that if the first object is created and the second one is not (say exception is thrown from number
constructor) then it's a memory leak - nobody will ever delete a pointer to the first number.
What can I do with this situation? I do not want to allocate objects sequentially and delete them if something has failed - it's too verbose.
I do not want to allocate objects sequentially and delete them if something has failed - it's too verbose.
Yes. You just need to apply smart pointer idiom more thoroughly; more precisely, change the parameter type to std::unique_ptr
, and use std::make_unique
(since C++14) (instead of using new
explicitly) to avoid this problem. e.g.
struct operation: equation {
operation(std::unique_ptr<equation> left, std::unique_ptr<equation> right)
: left(std::move(left)), right(std::move(right)) {}
private:
std::unique_ptr<equation> left, right;
};
then
operation op(std::make_unique<number>(123), std::make_unique<number>(456));
Note that the using of std::make_unique
is important here, the raw pointer created inside std::make_unique
is guaranteed to be managed by the returned std::unique_ptr
; even the 2nd std::make_unique
fails the
std::unique_ptr
created by the 1st std::make_unique
will see to it that the pointer it owns is destroyed. And this is also true for the case that the 2nd std::make_unique
is invoked first.
Before C++14 you can make your own version of std::make_unique
; a basic one is easy to write. Here's a possible implementation.
// note: this implementation does not disable this overload for array types
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}