多态unique_ptr的数组

时间:2014-07-17 16:06:56

标签: c++

使用唯一指针,我们可以写

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.
}

但是,如果类BC需要拥有自己的类型相关的自定义删除器:

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
}

有没有办法实现我在这里要做的事情?

2 个答案:

答案 0 :(得分:2)

您的第一个示例有未定义的行为,因为A没有virtual析构函数。 unique_ptr的默认删除者std::default_delete是无状态的,因此unique_ptr<A>始终会调用delete p;,其中p的静态类型为{{} 1}}。

您的代码会进行编译,因为A*副本构造函数可以接受另一个default_delete<T>实例,只要default_delete<U>可以隐式转换为U*,在这种情况下就是T*。但是,当基类析构函数不是virtual时,通过基类指针对派生类对象进行多态删除是未定义的行为。


每种类型的自定义删除的第二个问题可以通过提供一个自定义删除器来解决,该删除器是一个lambda,它首先将参数转换为适当的类型,然后再将其传递给该对象的删除器。

例如,

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)); }));

vector超出范围时,会打印:

void CustomDeleter(T*) [with T = A]
void CustomDeleter(T*) [with T = B]
void CustomDeleter(T*) [with T = C]

Live demo

答案 1 :(得分:1)

不,自定义删除器的类型是std::unique_ptr的模板参数。最简单的解决方案是将成员变量添加到自定义删除器:

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;
            //...
        }
    }
};

然后让CustomAlloc构建相应的custom_deleter并将其作为第二个参数传递给std::unique_ptr<A, custom_deleter>的构造函数。