You need to supply your own delete to the smart pointer creation when the standard delete
is not appropriate for deallocating, releasing, discarding, or otherwise disposing of a resource whose life time is being governed by the smart pointer.
A typical use of a smart pointer is to allocate memory as the resource being managed by the smart pointer so that when the smart pointer goes out of scope, the resource being managed, in this case memory, is discarded by using the delete
operator.
The standard delete
operator does two things: (1) call the object's destructor to allow the object to do any cleanup it needs to do before it's allocated memory is released or deallocated and (2) releases the memory that was allocated by the standard new
operator for the object when it was constructed. This is the reverse order of what happens with the new
operator which (1) allocates memory for the object and does the basic initialization needed to establish the construction environment for the object and (2) calls the constructor of the object to create the object's starting state. See What does the C++ new operator do other than allocation and a ctor call?
So the key question for needing your own deleter is "what actions that were done before the object constructor was invoked need to be unwound and backed out after the object's destructor completes?".
Normally this is a memory allocation of some kind such as is done by the standard new
operator.
However in the case of some resource other than memory allocated with the new
operator, the use of the delete
operator is not appropriate since the resource is not memory that was allocated by using the new
operator.
So when using a smart pointer for this kind of resource where the delete
operator is not appropriate, you need to supply your own deleter method or function or operator which the smart pointer will use when it goes out of scope and triggers its own destructor which will in turn handle the discarding of any resources that are being managed by the smart pointer.
A simple example with output
I put together a simple example with std::unique_ptr<>
along with the output generated to show using and not using a deleter with the pointer as well as showing explicit use of the destructor.
The source code of a simple Windows console application looks like:
// ConsoleSmartPointer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <memory>
#include <string>
#include <iostream>
class Fred {
public:
Fred() { std::cout << " Fred Constructor called." << std::endl; }
~Fred() { std::cout << " Fred Destructor called." << std::endl; }
};
class George {
public:
George() { std::cout << " George Constructor called" << std::endl; }
~George() { std::cout << " George Destructor called" << std::endl; }
private:
int iSomeData;
std::string a_label;
Fred myFred;
};
void cleanupGeorge(George *)
{
// just write out a log and do not explicitly call the object destructor.
std::cout << " cleanupGeorge() called" << std::endl;
}
void cleanupGeorge2(George *x)
{
// write out our message and then explicitly call the destructor for our
// object that we are the deleter for.
std::cout << " cleanupGeorge2() called" << std::endl;
x->~George(); // explicitly call destructor to do cleanup.
}
int func1()
{
// create a unique_ptr<> that does not have a deleter.
std::cout << "func1 start. No deleter." << std::endl;
std::unique_ptr<George> p(new George);
std::cout << "func1 end." << std::endl;
return 0;
}
int func2()
{
// create a unique_ptr<> with a deleter that will not explicitly call the destructor of the
// object created.
std::cout << "func2 start. Special deleter, no explicit destructor call." << std::endl;
std::unique_ptr<George, void(*)(George *)> p(new George, cleanupGeorge);
std::cout << "func2 end." << std::endl;
return 0;
}
int func3()
{
// create a unique_ptr<> with a deleter that will trigger the destructor of the
// object created.
std::cout << "func3 start. Special deleter, explicit destructor call in deleter." << std::endl;
std::unique_ptr<George, void(*)(George *)> p(new George, cleanupGeorge2);
std::cout << "func3 end." << std::endl;
return 0;
}
int main()
{
func1();
func2();
func3();
return 0;
}
The above simple application generates the following output:
func1 start. No deleter.
Fred Constructor called.
George Constructor called
func1 end.
George Destructor called
Fred Destructor called.
func2 start. Special deleter, no explicit destructor call.
Fred Constructor called.
George Constructor called
func2 end.
cleanupGeorge() called
func3 start. Special deleter, explicit destructor call in deleter.
Fred Constructor called.
George Constructor called
func3 end.
cleanupGeorge2() called
George Destructor called
Fred Destructor called.
Additional posts
What is a smart pointer and when should I use one?
Using custom deleter with std::shared_ptr
See as well this discussion about deleter with std::make_shared<>
and why it isn't available. How to pass deleter to make_shared?
Is custom deleter for std::unique_ptr a valid place for manual call to destructor?
When does std::unique_ptr<A> need a special deleter if A has a destructor?
RAII and smart pointers in C++