2013-09-27 94 views
80

我有一个unique_ptr成员的类。如何在std :: unique_ptr成员中使用自定义删除器?

class Foo { 
private: 
    std::unique_ptr<Bar> bar; 
    ... 
}; 

Bar是一个具有create()函数和destroy()函数的第三方类。

如果我想使用与它std::unique_ptr在一个独立的功能,我可以这样做:

void foo() { 
    std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); }); 
    ... 
} 

有没有办法用std::unique_ptr作为类的成员,要做到这一点?

回答

78

假设createdestroy是免费的功能(这似乎是从OP的代码片段的情况下),具有以下特征:

Bar* create(); 
void destroy(Bar*); 

你可以写你Foo类这样

class Foo { 

    std::unique_ptr<Bar, void(*)(Bar*)> ptr_; 

    // ... 

public: 

    Foo() : ptr_(create(), destroy) { /* ... */ } 

    // ... 
}; 

请注意,您不需要在此处编写任何lambda或定制删除程序,因为destroy已经是删除程序。

+86

使用C++ 11 'std :: unique_ptr ptr_;' – Joe

3

您可以简单地使用std::bind与您的销毁功能。

std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy, 
    std::placeholders::_1)); 

但是当然你也可以使用lambda。

std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);}); 
33

你只需要创建一个缺失者类:

struct BarDeleter { 
    void operator()(Bar* b) { destroy(b); } 
}; 

,并提供它作为unique_ptr模板参数。你仍然必须初始化在构造函数中的unique_ptr:

class Foo { 
    public: 
    Foo() : bar(create()), ... { ... } 

    private: 
    std::unique_ptr<Bar, BarDeleter> bar; 
    ... 
}; 

据我知道,所有的流行的C++库正确地实现这一点;由于BarDeleter实际上没有任何状态,所以不需要占用unique_ptr中的任何空间。

+4

这个选项是唯一适用于数组,std :: vector和其他集合的选项,因为它可以使用零参数std :: unique_ptr构造函数。其他答案使用的解决方案无法访问此零参数构造函数,因为在构造唯一指针时必须提供Deleter实例。但是这个解决方案向std :: unique_ptr('std :: unique_ptr ')提供了一个Deleter类(struct BarDeleter),它允许std :: unique_ptr构造函数自己创建一个Deleter实例。即允许下面的代码:'std :: unique_ptr bar [10];' – DavidF

+5

我将创建一个typedef以便于使用'typedef std :: unique_ptr UniqueBarPtr' – DavidF

60

可以在C++ 11中使用lambda干净地进行此操作(在G ++ 4.8.2中测试)。

鉴于这种可重复使用的typedef

template<typename T> 
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>; 

你可以写:

deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); }); 

例如,一个FILE*

deleted_unique_ptr<FILE> file(
    fopen("file.txt", "r"), 
    [](FILE* f) { fclose(f); }); 

有了这个,你得到的使用RAII进行异常安全清理的好处,无需尝试/捕捉噪音。

+1

这应该是答案,imo。这是一个更美丽的解决方案。或者是否有任何缺点,例如在定义中有'std :: function'或类似的东西? – j00hi

+6

@ j00hi,在我看来这个解决方案由于'std :: function'有不必要的开销。与此解决方案不同,Lambda或自定义类可以内联。但是,如果您想要隔离专用模块中的所有实现,则此方法具有优势。 – magras

+2

如果std :: function构造函数抛出会造成内存泄漏(如果lambda太大而不能放入std :: function对象内,可能会发生这种情况) – Ivan

相关问题