2016-11-15 99 views
4

我正在閱讀Scott Meyers的Effective Modern C++,他在討論使用pimpl成語並指向unique_ptr的實現類,但存在特殊成員函數的問題(例如作爲析構函數)要求類型完整。這是因爲unique_ptr的默認刪除程序在使用delete p之前會靜態聲明要刪除的類型是否已完成。因此,必須在實現文件中定義該類的任何特殊成員函數(而不是由編譯器生成),之後的實現類已定義。使用shared_ptr使用不完整類型的Pimpl成語

在本章的最後,他提到如果使用的智能指針是shared_ptr,則不需要在實現文件中定義特殊的成員函數,這源於它支持定製刪除器的方式。引述:

的差異的std ::的unique_ptr和std :: shared_ptr的之間的行爲 平普爾指針從這些智能指針支持自定義 刪除器的不同方式造成的。對於std :: unique_ptr,刪除器的類型是指針類型的一部分,這使編譯器可以生成更小的運行時數據結構和更快的運行時代碼。這種效率更高的結果是,當使用編譯器生成的特殊功能(例如, 析構函數或移動操作)時,指向類型必須完整。對於std :: shared_ptr, 刪除器的類型不是智能指針類型的一部分。這需要較大的運行時間數據結構和較慢的代碼,但是當編譯器生成的特殊功能被採用時,指向類型不需要是完整的 。

儘管如此,我還是不明白爲什麼shared_ptr仍然可以工作,沒有類是完整的。這似乎是使用shared_ptr時沒有編譯器錯誤的唯一原因是因爲沒有像unique_ptr這樣的靜態斷言,並且未定義的運行時行爲可能因爲缺少斷言而發生。

我不知道shared_ptr的析構函數的實現,但是(從閱讀C++入門),我收集了它的工作原理是這樣的印象:

del ? del(p) : delete p; 

del是一個指針或函數對象自定義刪除者。 Cppreference也使得它清除shared_ptr析構函數沒有定製刪除使用delete p

3)使用delete表達式delete ptr如果T不是數組類型; ... Y必須是完整的類型。刪除表達式必須結構合理,具有定義良好的行爲並且不會拋出任何異常。

重點強調刪除類型必須完整。PIMPL方法的一個最小的例子:

//widget.h 

#ifndef WIDGET 
#define WIDGET 

#include <memory> 

class Widget{ 
public: 
    Widget(); 
private: 
    struct Impl; 
    std::shared_ptr<Impl> pImpl; 

}; 

#endif // WIDGET 

//widget.cpp 

#include <string> 
#include "Widget.h" 

struct Widget::Impl{ 
    std::string name; 
}; 

Widget::Widget(): pImpl(new Impl) {} 

//main.cpp 

#include <iostream> 
#include "Widget.h" 

int main(){ 
    Widget a; 
} 

Widget amain.cpp被編譯,的shared_ptr模板instantited爲Widget類型(內main.cpp)和推測爲shared_ptr所得編譯析構函數包含線delete pImpl的執行,因爲我沒有提供自定義的deletor。但是在那個時候,Impl仍然沒有被定義,但是行delete pImpl被執行。這當然是未定義的行爲?

那麼,如何在shared_ptr下使用pimpl習語時,我不必在實現文件中定義特殊的成員函數來避免未定義的行爲?這裏創建

回答

6

爲共享指針的刪除器:

Widget::Widget(): pImpl(new Impl) {} 

直到該點,所有共享指針是一個std::funciton<void(Impl*)>的等價物。

當您使用T*構造shared_ptr時,它會寫入一個清除程序並將其存儲在std::function等效項中。此時該類型必須完整。

所以你必須定義的Impl完全定義的唯一功能是從某種T*創建pImpl

+0

我明白了,所以我假設'shared_ptr'析構函數直接調用delete(如果沒有提供自定義)是錯誤的,它仍然通過函數對象調用它。謝謝。 – SergeantPenguin

+0

儘管我不得不要求在我的腦海裏清楚這一點,但當您說刪除者是在這裏創建的時,我認爲這是通過關閉來完成的嗎?我找不到'shared_ptr'確切的默認deletor的詳細信息(儘管我看到'unique_ptr'使用'std :: default_delete')。 – SergeantPenguin

+0

@searg效果被指定,而不是實現。 – Yakk

相關問題