2015-05-06 2 views
1

Я пытаюсь определить существующий код, который использует члены данных «pimpl» для определения с помощью unique_ptr. Некоторые объекты требуют пользовательского удаления, а другие нет.std :: unique_ptr для элемента данных класса ABI (Ипподром Pimpl)

unique_ptr (в отличие от shared_ptr) destructor требует знания полного типа объекта. Таким образом, вы должны указать Deleter в объявлении члена данных:

class Foo { 
public: 
    ... 
    ~Foo (void) //in source file =default 
private: 
    class FooImpl; 
    std::unique_ptr <FooImpl> _pimpl; 
}; 

Когда инстанцированию Pimpl вы ограничены в использовании Deleter по умолчанию. Если вы хотите пользовательский Deleter, необходимо указать его в декларации

class Foo { 
public: 
    ... 
    ~Foo (void) //in source file =default 
private: 
    class FooImpl; 
    std::unique_ptr <FooImpl, std::function <void (FooImpl*&)> > _pimpl; 
}; 

Однако, вы не можете иметь возможность быть гибкими, хотите ли вы unique_ptr d'Tor с поведением по умолчанию или пользовательские Deleter. Более гибкий вариантом является вторым вариантом, но если вы хотите сохранить с поведением по умолчанию, то вы должны создать экземпляр unique_ptr с определенным Deleter, что эквивалентно по умолчанию удалить, например:

Foo::Foo (void) : 
    _impl (new CurveItemWidgetImpl, std::default_delete <FooImpl>()) { 
} 

Итак, std :: unique_ptr лучший способ обработки ABI (по сравнению с shared_ptr или raw pointer)?

+1

Не используйте 'std :: function' как' unique_ptr' deleter. Его конструктор может бросить. –

+0

@ T.C, можете ли вы дать другой способ обобщить делетера? – chook

+0

* Почему бы вам не хотеть ничего, кроме 'default_delete', как' pImpl' deleter? – ecatmur

ответ

3

Вы можете легко поставить Deleter, который эффективно вызывает непрозрачный символ:

class Foo { 
public: 
    ~Foo(); // = default 
private: 
    class FooImpl; 
    struct FooImplDelete { void operator()(FooImpl*); }; 
    std::unique_ptr<FooImpl, FooImplDelete> _pimpl; 
}; 

Теперь вы можете переместить определение Foo::FooImplDelete::operator() в исходном файле. Действительно, оптимизирующий компилятор будет встроить его в деструктор Foo.

Если у вас нет особых причин подозревать, что пользовательский делектор потребуется, вы также можете использовать значение по умолчанию; если понадобится изменить пользовательский DeleteR к releaseunique_ptr:

Foo::Foo() try 
    : _pimpl(new FooImpl) 
{ 
} 
catch(...) 
{ 
    delete _pimpl.release(); 
} 

Foo::~Foo() 
{ 
    delete _pimpl.release(); 
} 
+0

Очень приятно. Кроме того, я хотел бы создать RAII, который применяет параметр пользовательского удаления для всех моих _pImpl. Поэтому мне нужно определить новый RAII с оператором Deleter(), который примет параметр void * (или, может быть, лучше использовать decltype). Можете ли вы сделать дальнейшее совершенствование? – chook

+0

Пример @RichardHodges: http://goo.gl/YFrI5g – ecatmur

+0

@ecatmur Я стою исправлено - отозвать предыдущие комментарии –

2

Вот 2 способа, совместимые и портативные.

Прежде всего, это просто управлять памятью (вы увидите, что это тривиально). Второй использует unique_ptr

Само удалось:

class Foo 
{ 
public: 
    Foo(); 

    Foo(Foo&& rhs) noexcept; 
    Foo(const Foo&) = delete; 
    Foo& operator=(Foo&& rhs) noexcept; 
    Foo& operator=(const Foo&) = delete; 
    ~Foo() noexcept; 
private: 
    class impl; 
    impl* _pimpl = nullptr; 
}; 

// implementation: 

class Foo::impl 
{ 
    // TODO: implement your class here 
}; 

// example constructor 
Foo::Foo() 
: _pimpl { new impl {} } 
{} 

Foo::Foo(Foo&& rhs) noexcept 
: _pimpl { rhs._pimpl } 
{ 
    rhs._pimpl = nullptr; 
} 

Foo& Foo::operator=(Foo&& rhs) noexcept 
{ 
    using std::swap; 
    swap(_pimpl, rhs._pimpl); 
} 

Foo::~Foo() noexcept 
{ 
    // perform custom actions here, like moving impl onto a queue 

    // or just delete it 
    delete _pimpl; 
} 

использованием unique_ptr:

foo.h:

#ifndef __included__foo__h__ 
#define __included__foo__h__ 

#include <memory> 

class Foo 
{ 
public: 
    Foo(); 
    ~Foo(); 
private: 
    class impl; 
    std::unique_ptr<impl, void (*) (impl*) noexcept> _pimpl; 
    static void handle_delete(impl*) noexcept; 
}; 


#endif 

foo.cpp:

#include "Foo.h" 

class Foo::impl 
{ 
    // TODO: implement your class here 
}; 

// example constructor 
Foo::Foo() 
: _pimpl { new impl, &Foo::handle_delete } 
{ 

} 

Foo::~Foo() = default; 

void Foo::handle_delete(impl* p) noexcept 
{ 
    // do whatever you need to do with p 

    // or just delete it 
    delete p; 
}