我正在閱讀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 a
在main.cpp
被編譯,的shared_ptr
模板instantited爲Widget
類型(內main.cpp
)和推測爲shared_ptr
所得編譯析構函數包含線delete pImpl
的執行,因爲我沒有提供自定義的deletor。但是在那個時候,Impl
仍然沒有被定義,但是行delete pImpl
被執行。這當然是未定義的行爲?
那麼,如何在shared_ptr
下使用pimpl習語時,我不必在實現文件中定義特殊的成員函數來避免未定義的行爲?這裏創建
我明白了,所以我假設'shared_ptr'析構函數直接調用delete(如果沒有提供自定義)是錯誤的,它仍然通過函數對象調用它。謝謝。 – SergeantPenguin
儘管我不得不要求在我的腦海裏清楚這一點,但當您說刪除者是在這裏創建的時,我認爲這是通過關閉來完成的嗎?我找不到'shared_ptr'確切的默認deletor的詳細信息(儘管我看到'unique_ptr'使用'std :: default_delete')。 – SergeantPenguin
@searg效果被指定,而不是實現。 – Yakk