2016-09-02 6 views

答えて

73

std::unique_ptrはそうではないstd::shared_ptrは、型消去を実装しているからです。


std::shared_ptrは型消去を実装しているので、それはまた別の興味深い特性、すなわちをサポートしています。それはではありませんは、クラステンプレートにテンプレートタイプ引数として、削除者のタイプを必要とします。彼らの宣言を見てください:

template<class T,class Deleter = std::default_delete<T> > 
class unique_ptr; 

template<class T> 
class shared_ptr; 

がそれを持っていない一方で、型パラメータとしてDeleterを有しています。

ここで問題は、shared_ptrは型消去を実装するのですか?さて、参照カウントをサポートする必要があり、これをサポートするために、ヒープからメモリを割り当てる必要があります。に割り当てなければならないので、さらに1ステップ進んでタイプイレーズ—ヒープ割り当ても必要です。だから、基本的にはただの機会に過ぎない!そのため、型消去の

std::shared_ptrは2つのことをサポートすることができます:

  • それはvoid*として任意の型のオブジェクトを格納することができ、まだまだ正確で適切に破壊にをオブジェクトを削除することができますデストラクタを呼び出す
  • 型の引数として型の引数として型の型が渡されません。ちょっとした自由度型の安全性を損なうことなくです。

いいえ。これは、std::shared_ptrの仕組みに関するすべてです。

ここで質問は、std::unique_ptrオブジェクトを格納するvoid*とすることができますか?さて、答えははい —あなたが引数として適切なdeleterを渡すことを条件とします。ここではそのようなデモは次のとおりです。

int main() 
{ 
    auto deleter = [](void const * data) { 
     int const * p = static_cast<int const*>(data); 
     std::cout << *p << " located at " << p << " is being deleted"; 
     delete p; 
    }; 

    std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter); 

} //p will be deleted here, both p ;-) 

出力(online demo):私は必要があります。私の場合は

959 located at 0x18aec20 is being deleted 

あなたはコメントで非常に興味深い質問を型を削除する型ですが、(ヒープ割り当ての代償を払って)同様に可能であるようです。基本的には、これは実際にスマートポインタの第3のタイプのためのニッチスポットがあることを意味する:タイプの消去を有する排他的所有権スマートポインタ。

@Steve Jessopが、私は実際にこれを試したことがありませんが、多分あなたはunique_ptrでデリータ型として適切なstd::functionを使用してそれを達成できるか。次のソリューション、

を提案しますかそれが実際に動作すれば、あなたは排他的所有権とタイプ消去されたDeleterを完了します。

はこの提案に続いて、私はこれを実装し、

using deleter_t = std::function<void(void *)>; 
using unique_void_ptr = std::unique_ptr<void, deleter_t>; 

template<typename T> 
auto deleter(void const * data) -> void 
{ 
    T const * p = static_cast<T const*>(data); 
    std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n"; 
    delete p; 
} 

template<typename T> 
auto unique_void(T * ptr) -> unique_void_ptr 
{ 
    return unique_void_ptr(ptr, &deleter<T>); 
} 

int main() 
{ 
    auto p1 = unique_void(new int(959)); 
    auto p2 = unique_void(new double(595.5)); 
    auto p3 = unique_void(new std::string("Hello World")); 
} 

出力(online demo):

{Hello World} located at [0x2364c60] is being deleted. 
{595.5} located at [0x2364c40] is being deleted. 
{959} located at [0x2364c20] is being deleted. 

希望は助けています。

+9

いいえ、+1。しかし、 'std :: unique_ptr 'が適切な 'D'を提供することによって依然として可能であることを明示的に述べることによって、より良いものにすることもできます。 – Angew

+1

@Angrew:いいです、私の質問に書かれていない本当の根本的な質問を見つけました;) –

+0

@Nawaz:ありがとうございます。私の場合は、型を削除する型を必要としますが、(ヒープ割り当ての代償を払って)同様に可能であるようです。基本的には、これは実際にスマートポインタの第3のタイプのためのニッチスポットがあることを意味しますか?タイプ消去の排他的所有権スマートポインタ? –

5

1つの理由は、shared_ptrの多くのユースケースの1つにあります。つまり、寿命指標またはセンチネルです。

これは、元のブーストドキュメントに記載された:

closure_targetは、このようなものである
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv) 
{ 
    auto closure_target = { closure, std::weak_ptr<void>(pv) }; 
    ... 
    // store the target somewhere, and later.... 
} 

void call_closure(closure_target target) 
{ 
    // test whether target of the closure still exists 
    auto lock = target.sentinel.lock(); 
    if (lock) { 
     // if so, call the closure 
     target.closure(); 
    } 
} 

struct active_object : std::enable_shared_from_this<active_object> 
{ 
    void start() { 
     event_emitter_.register_callback([this] { this->on_callback(); }, 
             shared_from_this()); 
    } 

    void on_callback() 
    { 
     // this is only ever called if we still exist 
    } 
}; 

struct closure_target { 
    std::function<void()> closure; 
    std::weak_ptr<void> sentinel; 
}; 

呼び出し側は、このようなコールバックのものを登録しますshared_ptr<X>は常にcoであるため

shared_ptr<void>に変換可能であるため、event_emitterは、コールバックしているオブジェクトの種類をわかりやすく認識できます。

この構成では、交差するケースを処理する義務のイベントエミッタへの加入者を解放します(コールバックがactive_objectがなくなるのを待っていればどうでしょうか?)。また、同期する必要もない登録解除。 weak_ptr<void>::lockは同期操作です。

関連する問題