
In this tutorial, we will learn how to create a custom deleted to delete a smart pointer in C++.
Introduction to Custom Deleter in C++
When a smart pointer of any user-defined type is destroyed, the destructor of the class is invoked giving us the ability to explicitly inspect and modify the delete process.
But that is not the case with smart pointers of a primitive type. Below is an example :
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
A(){
cout << "Constructor called \n";
}
~A(){
cout << "Destructor called\n";
}
};
int main()
{
//user defined smart pointer object
shared_ptr<A> ptr { new A()};
//primitive smart pointer object
shared_ptr<int> num {new int{5}};
return 0;
}
Output
Constructor Called
Destructor Called
As we can see, the destructor of the A class pointer object was called when its instance got destroyed.
We can add additional code in that destructor to do extra things than just printing.The same is not
possible in the case of the primitive smart pointer because it has not got any destructor.For this
purpose ,we need to use custom deleter.
We can write a specific type of custom delete function that will be called automatically when any smart pointer of that particular type is deleted.We will see an example of the same, but before that let’s see how can we create a custom delete in C++.
Syntax:
return_type <function_name> (<datatype>* <object_name>){
//code
}
Let’s see how to use smart pointers Unique Pointer and Shared Pointer with custom deleter.
Custom Deleter with unique_ptr:
unique_ptr uses the standard new and delete operators to allocate and deallocate memory. You can change this behavior as follows:
int* malloc_int(int value)
{
int* p = (int*)malloc(sizeof(int));
*p = value;
return p;
}
int main()
{
unique_ptr<int, decltype(free)*> myIntSmartPtr(malloc_int(42), free);
return 0;
}
This code allocates memory for an integer with malloc_int(). The unique_ptr deallocates the
memory by calling the standard free() function. Since in C++ you should never use malloc(),
but new instead. However, this feature of unique_ptr is available because it is useful to manage
other resources instead of just memory. For example, it can be used to automatically close a file
or network socket or anything when the unique_ptr goes out of scope.
Unfortunately, the syntax for a custom deleter with unique_ptr is a bit clumsy. You need to specify
the type of your custom deleter as a template type parameter. In this example, decltype(free) is
used which returns the type of free(). The template type parameter should be the type of a pointer
to a function, so an additional * is appended, as in decltype(free)*. Using a custom deleter
with shared_ptr is much easier.
Custom Deleter with shared_ptr:
You use shared_ptr in a similar way to unique_ptr. To create one, you use make_shared(), which
is more efficient than creating a shared_ptr directly.
auto mySimpleSmartPtr = make_shared();
Just like unique_ptr, shared_ptr by default uses the standard new and delete operators to allocate
and deallocate memory,
// Implementation of malloc_int() as before. shared_ptr<int> myIntSmartPtr(malloc_int(42), free);
As one can observe, we don’t have to specify the type of the custom deleter as a template type parameter, so this makes it much easier than a custom deleter with unique_ptr.
The following example uses a shared_ptr to store a file pointer. When the shared_ptr is
reset (in this case when it goes out of scope), the file pointer is automatically closed with a
call to CloseFile().
void CloseFile(FILE* filePtr)
{
if (filePtr == nullptr)
return;
fclose(filePtr);
cout << "File closed." << endl;
}
int main()
{
FILE* f = fopen("data.txt", "w");
shared_ptr<FILE> filePtr(f, CloseFile);
if (filePtr == nullptr)
{
cerr << "Error opening file." << endl;
}
else
{
cout << "File opened." << endl;
// Use filePtr
}
return 0;
}
I hope you find this article useful.