Using unique pointers, we can write
class A{};
class B: public A{};
class C: public A{};
std::vector<std::unique_ptr<A>> myArray;
//
f()
{
std::unique_ptr<B> pB {new B()};
std::unique_ptr<C> pC {new C()};
myArray.push_back(std::move(pB));
myArray.push_back(std::move(pC));
// nice! now I can use the interface of A without
// worrying about its subclasses.
}
However, if classes B
and C
need to have their own type-dependent custom deleter:
class A{};
class B: public A{};
class C: public A{};
template<class T>
struct custom_deleter
{ // roughly the same implementation as std::default_deleter
void operator()(T* p)
{
CustomFree(p);
}
};
//
std::vector<std::unique_ptr<A>> myArray;
//
f()
{
std::unique_ptr<B, custom_deleter<B>> pB {CustomAlloc(B())};
std::unique_ptr<C, custom_deleter<C>> pC {CustomAlloc(C())};
myArray.push_back(std::move(pB)); // error: can't convert to default deleter
myArray.push_back(std::move(pC)); // item dito
}
Is there a way to achieve what I'm trying to do here?
Your first example has undefined behavior because A
doesn't have a virtual
destructor. unique_ptr
's default deleter, std::default_delete
is stateless, so a unique_ptr<A>
will always call delete p;
, where the static type of p
is A*
.
Your code compiles because the default_delete<T>
copy constructor can accept another default_delete<U>
instance as long as U*
is implicitly convertible to T*
, which it is in this case. However, polymorphic deletion of the derived class object through base class pointer, when the base class destructor is not virtual
is undefined behavior.
The second problem of custom deleters for each type can be solved by supplying a custom deleter that is a lambda which first casts the argument to the appropriate type before passing it on to that object's deleter.
For instance,
template<typename T>
void CustomDeleter(T *t)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
delete t;
}
std::vector<std::unique_ptr<A, void(*)(A *)>> myArray;
myArray.push_back(
std::unique_ptr<A, void(*)(A *)>(new A(), [](A *a){
CustomDeleter(a); }));
myArray.push_back(
std::unique_ptr<A, void(*)(A *)>(new B(), [](A *a){
CustomDeleter(static_cast<B*>(a)); }));
myArray.push_back(
std::unique_ptr<A, void(*)(A *)>(new C(), [](A *a){
CustomDeleter(static_cast<C*>(a)); }));
When the vector
goes out of scope, this prints:
void CustomDeleter(T*) [with T = A]
void CustomDeleter(T*) [with T = B]
void CustomDeleter(T*) [with T = C]
No, the type of the custom deleter is a template parameter to std::unique_ptr
. Your easiest solution would be to add a member variable to the custom deleter:
struct custom_deleter
{
int m_kind;
custom_deleter(int kind)
{
m_kind = kind;
}
void operator()(A* p)
{
switch (m_kind)
{
case 0:
CustomFree0(p);
break;
case 1:
CustomFree1(p);
break;
//...
}
}
};
And then make CustomAlloc
build the appropriate custom_deleter
and pass it to the constructor of std::unique_ptr<A, custom_deleter>
as second parameter.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.