2012-01-26 179 views
54

下面是我在嘗試爲pimpl使用unique_ptr時看到的簡化。我選擇unique_ptr是因爲我真的希望類擁有指針 - 我希望pimpl指針和類的生命週期相同。如何爲pimpl使用unique_ptr?

不管怎麼說,這是標題:

#ifndef HELP 
#define HELP 1 

#include <memory> 

class Help 
{ 

public: 

    Help(int ii); 
    ~Help() = default; 

private: 

    class Impl; 
    std::unique_ptr<Impl> _M_impl; 
}; 

#endif // HELP 

這是源:

#include "Help.h" 

class Help::Impl 
{ 
public: 
    Impl(int ii) 
    : _M_i{ii} 
    { } 

private: 

    int _M_i; 
}; 

Help::Help(int ii) 
: _M_impl{new Help::Impl{ii}} 
{ } 

我可以編譯這些到庫就好了。但是,當我嘗試在測試程序來使用它,我得到

[email protected]:~/ext_distribution$ ../bin/bin/g++ -std=c++0x -o test_help test_help.cpp Help.cpp 
In file included from /home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/memory:86:0, 
       from Help.h:4, 
       from test_help.cpp:3: 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Help::Impl]': 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:245:4: required from 'void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>; std::unique_ptr<_Tp, _Dp>::pointer = Help::Impl*]' 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:169:32: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>]' 
Help.h:6:7: required from here 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:63:14: error: invalid application of 'sizeof' to incomplete type 'Help::Impl' 

這是一個衆所周知的safety feature。我試圖遵循。

我的問題是,如果我把頭文件中的Help :: Impl聲明,它似乎會消除pimpl的任何優勢。類佈局對用戶可見。該定義是隱藏的,但我可以通過幫助類和私人成員完成。另外,包括Impl的聲明還會帶來新的標題,我希望將它們分開。

我錯過了什麼?人們在Impl聲明中放置什麼?我在做幫助嗎?錯了嗎?哎呀!

+2

另請參閱[GotW#101:編譯防火牆,第2部分](http://herbsutter.com/gotw/_101/)和[與此相關的問題](http://stackoverflow.com/q/8595471/636019)。 – ildjarn

+1

儘管這是一個老問題,但可能需要指出的是,[如cppreference](http://en.cppreference.com/w/cpp/language/pimpl)中所述,用'unique_ptr'實現的PImpl應該被包裝在類似['propagate_const'](http://en.cppreference.com/w/cpp/experimental/propagate_const)的東西中以獲得完整的正確性。 – jdehesa

+1

@jdehesa謝謝你,我一直在尋找propagate_const作爲解決某些API尷尬的方法。我幾乎想知道在這個意義上,unique_ptr是否被默認破壞。好像propagate_const應該是內置的,或者至少是默認的。 – emsr

回答

69

我相信你的test_help.cpp實際上會看到你聲明爲default的~Help()析構函數。在該析構函數中,編譯器也嘗試生成unique_ptr析構函數,但它需要Impl聲明。

因此,如果您將析構函數定義移至Help.cpp,則此問題應該消失。從unique_ptr定義

Help::~Help() = default; 
+9

好的,在標題中,我只是*宣佈了幫助的dtor。然後在實現文件中,我把Help ::〜Help()= default;活泉!謝謝。一切正常! – emsr

+0

優秀的東西 –

+4

智能指針,包括'unique_ptr'需要使用不完整的類型。 「刪除」功能是在構建時分配的,因此指針的用戶不需要知道它是如何被刪除的。如果解決了這個問題,這意味着他的'unique_ptr'實現被破壞了。 –

1

注意這一點::

的std ::的unique_ptr

- - 編輯 可以定義析構函數被默認在cpp文件,也可以針對不完整的類型T構造,例如以便於用作pImpl習語中的句柄。如果使用缺省刪除器,則T必須在調用刪除器的代碼中完成,這發生在析構函數中,移動賦值運算符並重置std :: unique_ptr的成員函數。