利用std::shared_ptr 回收不同型別物件的記憶體
轉自: http://stackoverflow.com/questions/5913396/why-do-stdshared-ptrvoid-work
利用std::function 進行deleter 函式的型別擦除。
===========================================ASK===========================================
I found some code using std::shared_ptr to perform arbitrary cleanup at shutdown. At first I thought this code could not possibly work, but then I tried the following:
#include <memory> #include <iostream> #include <vector> class test { public: test() { std::cout << "Test created" << std::endl; } ~test() { std::cout << "Test destroyed" << std::endl; } }; int main() { std::cout << "At begin of main.\ncreating std::vector<std::shared_ptr<void>>" << std::endl; std::vector<std::shared_ptr<void>> v; { std::cout << "Creating test" << std::endl; v.push_back( std::shared_ptr<test>( new test() ) ); std::cout << "Leaving scope" << std::endl; } std::cout << "Leaving main" << std::endl; return 0; }
This program gives the output:
At begin of main.
creating std::vector<std::shared_ptr<void>>
Creating test
Test created
Leaving scope
Leaving main
Test destroyed
I have some ideas on why this might work, that have to do with the internals of std::shared_ptrs as implemented for G++. Since these objects wrap the internal pointer together with the counter the cast from std::shared_ptr<test>
std::shared_ptr<void>
is
probably not hindering the call of the destructor. Is this assumption correct?
And of course the much more important question: Is this guaranteed to work by the standard, or might further changes to the internals of std::shared_ptr, other implementations actually break this code?
===========================================ANSWER=======================================
The trick is that std::shared_ptr
performs
type erasure. Basically, when a new shared_ptr
is
created it will store internally a deleter
function
(which can be given as argument to the constructor but if not present defaults to calling delete
).
When the shared_ptr
is
destroyed, it calls that stored function and that will call the deleter
.
A simple sketch of the type erasure that is going on simplified with std::function, and avoiding all reference counting and other issues can be seen here:
template <typename T>
void delete_deleter( void * p ) {
delete static_cast<T*>(p);
}
template <typename T>
class my_unique_ptr {
std::function< void (void*) > deleter;
T * p;
template <typename U>
my_unique_ptr( U * p, std::function< void(void*) > deleter = &delete_deleter<U> )
: p(p), deleter(deleter)
{}
~my_unique_ptr() {
deleter( p );
}
};
int main() {
my_unique_ptr<void> p( new double ); // deleter == &delete_deleter<double>
}
// ~my_unique_ptr calls delete_deleter<double>(p)
When a shared_ptr
is
copied (or default constructed) from another the deleter is passed around, so that when you construct a shared_ptr<T>
from
a shared_ptr<U>
the
information on what destructor to call is also passed around in the deleter
.