Since C++11, because of several reasons, developers tend to use smart pointer classes for dynamic lifetime objects. And with those new smart pointer classes, standards, even
You can't. make_shared<T>
forwards the provided arguments to the constructor of type T
. It is used for the simple case when you want the default deleter.
It is unspecified how make_shared
obtains the memory for the object (it could use operator new
or malloc
or some kind of allocator) so there is no way a custom deleter could know how to do the right thing. make_shared
creates the object, so you have to also rely on it to destroy the object correctly and do the appropriate clean up, whatever that is.
Also we would like to pass a custom deleter to smart pointer classes,
shared_ptr<int> p(new int(12), deleter);
I don't think this is a very realistic example. A custom deleter is typically used when the resource was obtained in some special way. If you just created it with new
like this then why do you need a custom deleter anyway?
If you just want some code to be run on destruction then put it in a destructor! That way you can also still use it with make_shared
e.g.
struct RunSomethingOnDestruction {
RunSomethingOnDestruction(int n) : i(n) { }
~RunSomethingOnDestruction() { /* something */ }
int i;
};
auto px = std::make_shared<RunSomethingOnDestruction>(12);
std:shared_ptr<int> p(px, px->i);
This gives you a shared_ptr<int>
that is created by make_shared
(so you get the memory optimisations done by make_shared
) that will run some custom code on destruction.
As other have said, make_shared
cannot be used with a custom deleter. But I want to explain why.
Custom deleters exist because you allocated the pointer in some special way, and therefore you need to be able to deallocate it in a correspondingly special way. Well, make_shared
allocates the pointer with new
. Objects allocated with new
should be deallocated with delete
. Which the standard deleter dutifully does.
In short, if you can live with the default allocation behavior, you can live with the default deallocation behavior too. And if you can't live with the default allocation behavior, you should use allocate_shared, which uses the provided allocator to both allocate and deallocate the storage.
Also, make_shared
is allowed to (and almost certainly will) allocate the memory for T
and the control block for the shared_ptr within the same allocation. This is something that your deleter can't really know about or deal with. Whereas allocate_shared
is capable of handling it, since the allocator you provide can do allocation and deallocation duties.
If you use a custom deleter you can't use make_unique
or make_shared
functions when you create a smart pointer objects . Since we need to provide our custom deleter these functions do not support that .
Don't use make_unique or make_shared if you need a custom deleter or adopting a raw pointer from elsewhere.
The idea is if you need a specialized way to delete your object you probably need a specialized way to create them too .
Let say we a class Test
#include <iostream>
using namespace std;
class Test
{
private :
int data;
public :
Test() :data{0}
{
cout << "Test constructor (" << data << ")" << endl;
}
Test(int d) : data{ d }
{
cout << "Test constructor (" << data << ")" << endl;
}
int get_data() const { return data; }
~Test()
{
cout << "Test Destructor (" << data << ')' << endl;
}
};
// main function.
int main()
{
// It's fine if you use make_shared and custom deleter like this
std::shared_ptr<Test> ptr(new Test{1000},
[](Test *ptr)
{
cout << "some Code that you want to execute ";
delete ptr;
});
return 0;
}
But if you use make_shared function you will get a compiler error
std::shared_ptr<Test> ptr = make_shared<Test>(1000,
[](Test *ptr){
cout << "some Code that you want to execute ";
delete ptr;
});
Basically make_shared function is a wrapper for new
and delete
and if you want a custom deleter you have to provide you own new
and delete
As from the documentation, make_shared
accepts a list of arguments with which an instance of T will be constructed.
Moreover, the documentation says that:
This function is typically used to replace the construction std::shared_ptr(new T(args...)) of a shared pointer from the raw pointer returned by a call to new.
Because of that, you can deduce that you can't set a custom deleter.
To do that, you have to create the shared_ptr
for yourself by means of the right constructor.
As an example of a constructor from the proposed list, you can use:
template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
Thus, the code will be something like:
auto ptr = std::shared_ptr(new MyClass{arg1, arg2}, myDeleter);
Instead of:
auto ptr = std::make_shared<MyClass>(arg1, arg2);