Summary
In this post, I will introduce custom deleters when using C++ std::shared_ptr
and std::unique_ptr
.
Conclusion
Use the following snippets. Note the fourth case, where a unique_ptr
with custom deleter will not pass the deleter to the shared_ptr
when using release()
.
class OldThing {
public:
~OldThing()
{
if (allocated_) {
std::cout << "MEMORY LEAK!" << std::endl;
}
}
void Deallocate()
{
std::cout << "Deallocate called." << std::endl;
allocated_ = false;
}
private:
bool allocated_{ true };
};
int main() {
{
OldThing oldThing;
// oldThing going out of scope causes a "memory leak"
}
{
// shared_ptr's constuctor handles the deleter
std::shared_ptr<OldThing> p(new OldThing,
[] (OldThing* ot) {
ot->Deallocate();
delete ot;
}
);
}
{
// We need to include the deleter's type in the template parameters:
std::unique_ptr<OldThing, void(*)(OldThing*)> p(new OldThing,
[] (OldThing* ot) {
ot->Deallocate();
delete ot;
}
);
}
{
// !!! memory leak!!!
std::unique_ptr<OldThing, void(*)(OldThing*)> p(new OldThing,
[] (OldThing* ot) {
ot->Deallocate();
delete ot;
}
);
auto p_ = std::shared_ptr<OldThing>(p.release());
}
{
// works, shared_ptr receives the deleter from unique_ptr
std::unique_ptr<OldThing, void(*)(OldThing*)> p(new OldThing,
[] (OldThing* ot) {
ot->Deallocate();
delete ot;
}
);
std::shared_ptr<OldThing> p_ = std::move(p);
}
return 0;
}
The result will be
MEMORY LEAK!
Deallocate called.
Deallocate called.
MEMORY LEAK!
Deallocate called.