簡體   English   中英

用於pImpl成語的std :: auto_ptr或boost :: shared_ptr?

[英]std::auto_ptr or boost::shared_ptr for pImpl idiom?

當使用pImpl習語時 ,最好使用boost:shared_ptr而不是std::auto_ptr 我確定我曾經讀過增強版更加異常友好嗎?

class Foo
{
public:
    Foo();
private:
    struct impl;
    std::auto_ptr<impl> impl_;
};

class Foo
{
public:
    Foo();
private:
    struct impl;
    boost::shared_ptr<impl> impl_;
};

[編輯]使用std :: auto_ptr <>是否總是安全的,或者是否需要使用替代的boost智能指針?

你不應該真的使用std :: auto_ptr。 在聲明std :: auto_ptr時,析構函數將不可見,因此可能無法正確調用它。 這假設您正在聲明您的pImpl類,並在另一個文件中的構造函數內創建實例。

如果您使用的boost :: scoped_ptr的 (不需要的shared_ptr在這里,你將不會被共享與任何其他對象的平普爾,這是由scoped_ptr的強制執行不可復制 ),你只需要在平普爾析構函數調用點看到scoped_ptr構造函數。

例如

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
};

// Body of these functions in MyClass.cpp

在這里,編譯器將生成MyClass的析構函數。 哪個必須調用auto_ptr的析構函數。 在實例化auto_ptr析構函數時,Pimpl是一個不完整的類型。 因此,當auto_ptr析構函數刪除Pimpl對象時,它將不知道如何調用Pimpl析構函數。

boost :: scoped_ptr(和shared_ptr)沒有這個問題,因為當你調用scoped_ptr(或reset方法)的構造函數時,它也會使一個函數指針等效,它將使用而不是調用delete。 這里的關鍵點是,當Pimpl不是不完整類型時,它會實例化釋放函數。 作為旁注,shared_ptr允許您指定自定義釋放函數,因此您可以將它用於GDI句柄或其他任何您可能想要的東西 - 但這對您的需求來說太過分了。

如果你真的想使用std :: auto_ptr,那么當完全定義Pimpl時,你需要確保在MyClass.cpp中定義你的MyClass析構函數。

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
    ~MyClass();
};

// in MyClass.cpp

#include "Pimpl.h"

MyClass::MyClass() : pimpl(new Pimpl(blah))
{
}

MyClass::~MyClass() 
{
    // this needs to be here, even when empty
}

編譯器將生成代碼,在空析構函數中有效地“破壞”所有MyClass成員。 因此,在實例化auto_ptr析構函數時,Pimpl不再是不完整的,編譯器現在知道如何調用析構函數。

就個人而言,我認為確保一切都正確是不值得的。 還有一種風險,即有人會稍后出現並通過刪除看似多余的析構函數來整理代碼。 因此,對於這種事情,使用boost :: scoped_ptr可以更安全。

我傾向於使用auto_ptr 一定要使你的類不可復制(聲明私有拷貝ctor和operator =,否則繼承boost::noncopyable )。 如果你使用auto_ptr ,你需要定義一個非內聯析構函數,即使正文是空的。 (這是因為如果你讓編譯器生成默認的析構函數,那么當生成對delete impl_的調用時, impl將是一個不完整的類型,調用未定義的行為)。

auto_ptr和boost指針之間幾乎無法選擇。 如果使用標准庫替代方案,我傾向於不在風格上使用boost。

std::auto_ptr的boost替代方法是boost::scoped_ptr auto_ptr的主要區別在於boost::scoped_ptr是不可復制的。

有關詳細信息,請參閱此頁面

boost :: shared_ptr專為pimpl習語而設計。 其中一個主要優點是它不允許為持有pimpl的類定義析構函數。 共享所有權政策可能既有利也有劣勢。 但在以后的情況下,您可以正確定義復制構造函數。

如果你真的很迂腐,那么使用auto_ptr成員並不需要在使用它時需要完全定義auto_ptr的模板參數。 話雖如此,我從未見過這不起作用。

一種變體是使用const auto_ptr 只要您可以在初始化列表中使用新表達式構造“pimpl”並保證編譯器無法生成默認的復制構造函數和賦值方法,這就可以正常工作。 仍然需要提供封閉類的非內聯析構函數。

在其他條件相同的情況下,我傾向於使用僅使用標准庫的實現,因為它使事物更便攜。

如果你想要一個可復制的類,使用scoped_ptr ,它禁止復制,因此默認情況下你的類很難使用錯誤(與使用shared_ptr相比,編譯器不會自己發出復制工具;如果是shared_ptr ,如果你不知道你做了什么[即使對於巫師也常常這樣],當突然有一些東西的副本也修改了某些東西時會有奇怪的行為,然后超出定義一個復制構造函數和復制賦值:

class CopyableFoo {
public:
    ...
    CopyableFoo (const CopyableFoo&);
    CopyableFoo& operator= (const CopyableFoo&);
private:
    scoped_ptr<Impl> impl_;
};

...
CopyableFoo (const CopyableFoo& rhs)
    : impl_(new Impl (*rhs.impl_))
{}

shared_ptr比pImpl的auto_ptr要好得多,因為你的外部類在復制時會突然失去指針。

使用shared_ptr,您可以使用向前聲明的類型,以便工作。 auto_ptr不允許向前聲明的類型。 scoped_ptr也沒有,如果你的外部類無論如何都是不可復制的並且只有一個指針,它也可能是常規的。

在pImpl中使用侵入式引用計數並獲得外部類來調用其副本並在其實現中分配語義有很多要說的。 假設這是一個真正的供應商(提供類)模型,供應商不會強迫用戶使用shared_ptr,或者使用相同版本的shared_ptr(boost或std)。

我對弗拉基米爾·巴托夫[修改]的impl_ptr非常滿意。 它使得創建pImpl變得非常容易,而無需制作顯式的復制構造函數和賦值運算符。

我修改了原始代碼,因此它現在類似於shared_ptr,因此可以在epilog代碼中使用,並且保持快速。

不要那么努力地用腳射擊自己,用C ++你有很多機會:)沒有必要使用任何一個自動指針,因為你完全知道什么時候你的對象應該進出生命(在你的生活中)構造函數和析構函數)。

把事情簡單化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM