问题
I can't understand how a shared_ptr
can store the deleter that I gave to it.
Initially, using a shared_ptr<int>
, i thought it might use an std::function<void(int*)>
, but i can give, as a deleter, any kind of function (or callable objects), as long as the first parameter is a int*
.
How can shared_ptr
do this?
I'm sorry if this is a silly question, I'm new to C++, forgive me!
Edit: The question is: how can I do something like that? What should I use? Any example? Or it is a very advanced topic?
回答1:
The deleter, as well as the allocator, are type-erased. The shared pointer manages a dynamically allocated, private, templated control object, which is accessed through a polymorphic base and which stores all the type-specific state and functionality.
The implementation of std::function
uses similar ideas, since it is also a type-erasing dynamic manager class, but both are typically implemented entirely separately.
The upshot is that both classes are comparatively "expensive" and should only be used when they are genuinely necessary. Otherwise, cheaper, non-polymorphic non-dynamic solutions are usually preferable.
回答2:
I can give, as a deleter, any kind of function (or callable objects), as long as the first parameter is a
int*
.
No, not really. The std::shared_ptr
constructor has the following contract, found in section 20.8.2.2.1 ([util.smartptr.shared.const]
):
template<class Y, class D> shared_ptr(Y* p, D d); template<class Y, class D, class A> shared_ptr(Y* p, D d, A a); template <class D> shared_ptr(nullptr_t p, D d); template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
Requires:
p
shall be convertible toT*
.D
shall be CopyConstructible. The copy constructor and destructor ofD
shall not throw exceptions. The expressiond(p)
shall be well formed, shall have well defined behavior, and shall not throw exceptions. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.Effects: Constructs a
shared_ptr
object that owns the objectp
and the deleterd
. The second and fourth constructors shall use a copy ofa
to allocate memory for internal use.Postconditions:
use_count() == 1 && get() == p
.Throws:
bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained.Exception safety: If an exception is thrown,
d(p)
is called.
This requirement is a lot stronger than that the first parameter of the deleter must be the right type. It has to be the only parameter (without a default argument), such that d(p)
is legal. This is slightly more flexible than std::function<void (int*)>
, because the return type can be anything, but it's also more constrained with respect to exception guarantees.
If your compiler doesn't catch you when you provide a deleter with multiple required parameters, the standard library implementation is doing something rather wrong.
As far as how to implement it, take advantage of the fact that it must be CopyConstructible. For example, the following lambda should work pretty nicely, and be assignable to std::function<void(void)>
(the CopyConstructible guarantee ensures that capture by value works):
[d, p] { d(p); }
来源:https://stackoverflow.com/questions/25655145/how-does-a-shared-ptr-store-deleter