I'm interested in updating an old personal project to modern C . I appreciate how RAII simplifies cleanup: instead of making a new
object and remembering to delete
it before every return point in a function, just make_unique
and it will be destroyed appropriately. But I have one nitpick when comparing the generated assembly.
Say there's a class method that replaces one of its unique_ptr
members with a new value:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo = std::make_unique<int>(x * 3 5);
}
This will create a new
int
, assign it to m_foo
, and then delete
the old value of m_foo
. But that's not quite the same as the old behavior, which could delete
the old value, then create a new
one and assign it to m_foo
:
// int *MyClass::m_foo;
void MyClass::refresh_foo(int x) {
delete m_foo;
m_foo = new int(x * 3 5);
}
From the Compiler Explorer, gcc, clang, and MSVC all generate less code for the old way than the new one. I understand why this is the case, since that's the order in which all expressions are evaluated; p = q;
has to construct q
first. And of course m_foo
had an invalid value in between the delete
and new
lines. But is there a way I'm missing to have unique_ptr
destroy its old value and then create a new one in one expression? A "replace_unique
"? Seems like it would be helpful when dealing with large objects so two of them don't needlessly coexist.
Edit: To be clear, I'm just using int
as a trivial example here. My actual use cases are with classes of my own or from a library. And m_foo.reset(new int(x * 3 5));
naturally has the same problem compared to the old way of doing things, since it too has to construct the new
int
before assigning it and delet
ing the previous one.
CodePudding user response:
You can use unique_ptr::reset()
to deterministically destroy the currently held object when you want. Update the unique_ptr
with a nullptr
pointer before then updating it with a new object pointer, eg:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo.reset(); // <- int is destroyed here
m_foo = std::make_unique<int>(x * 3 5);
}
CodePudding user response:
I accepted the answer which calls m_foo.reset()
before m_foo = std::make_unique<int>(...)
because it's the simplest way to solve the problem as stated: destroying the original value before constructing the new one. However, that does result in an extra delete
call which shouldn't be necessary and is less optimal than the "pre-modern" way of using raw pointers.
This method, at least in Compiler Explorer with -Ofast
, avoids the second delete
call, and only has one extra mov
instruction compared to the old way (explicitly setting m_foo
to nullptr
before assigning its newly constructed value):
// std::unique_ptr<int> MyClass::m_foo;
void refresh_smart(int x) {
m_foo = nullptr;
auto new_foo = std::make_unique<int>(x * 3 5);
m_foo.swap(new_foo);
new_foo.release(); // no memory leak since it's null
}