shared_ptrマジック:)


89

リドストローム氏と私は議論をしました:)

Lidström氏の主張は、構成shared_ptr<Base> p(new Derived);はBaseに仮想デストラクタを必要としないというものです。

Armen Tsirunyan:「本当に?shared_ptrは正しくクリーンアップされますか?この場合、その効果をどのように実装できるかを示していただけませんか?」

DanielLidström:「shared_ptrは独自のデストラクタを使用してConcreteインスタンスを削除します。これはC ++コミュニティではRAIIと呼ばれます。RAIIについてできる限りのことを学ぶことをお勧めします。これにより、C ++コーディングが非常に簡単になります。あらゆる状況でのRAII。」

Armen Tsirunyan:「RAIIについて知っています。また、pnが0に達すると、最終的にshared_ptrデストラクタが保存されたpxを削除する可能性があることも知っています。しかし、pxに静的型ポインタBaseと動的型ポインタがあるDerived場合Base、仮想デストラクタがない限り、これは未定義の動作になります。間違っている場合は修正してください。」

DanielLidström:「shared_ptrは静的型がConcreteであることを知っています。コンストラクターに渡したので、これを知っています。魔法のように見えますが、設計によるものであり、非常に優れていると確信できます。」

だから、私たちを判断してください。ポリモーフィッククラスに仮想デストラクタを持たせることなく、shared_ptrを実装することはどのように可能ですか?前もって感謝します


3
元のスレッドにリンクできたはずです。
ダリンディミトロフ

8
もう1つの興味深い点はshared_ptr<void> p(new Derived)Derivedオブジェクトがそうであるvirtualかどうかに関係なく、そのデストラクタによってオブジェクトも破壊されることです。
dalle 2010年

7
質問をする素晴らしい方法:)
rubenvb 2010年

5
shared_ptrでこれが許可されていても、仮想dtorなしでクラスをベースとして設計することは非常に悪い考えです。RAIIに関するDanielのコメントは誤解を招く可能性がありますが、これとは関係ありませんが、引用された会話は単純な誤解のように聞こえます(そしてshared_ptrがどのように機能するかについての誤った仮定)。

6
RAIIではなく、デストラクタをタイプ消去します。shared_ptr<T>( (T*)new U() )どこstruct U:Tが正しいことをしないので、注意する必要があります(これは、を取り、T*渡される関数など、間接的に簡単に行うことができますU*
Yakk-Adam Nevraumont 2014

回答:


74

はい、そのようにshared_ptrを実装することは可能です。Boostは機能し、C ++ 11標準もこの動作を必要とします。追加の柔軟性として、shared_ptrは単なる参照カウンター以上のものを管理します。いわゆるデリータは通常、参照カウンタも含む同じメモリブロックに配置されます。しかし、楽しい部分は、このdeleterの型がshared_ptr型の一部ではないということです。これは「型消去」と呼ばれ、基本的に「ポリモーフィック関数」を実装するために使用される手法と同じです。boost:: functionまたはstd :: functionは実際のファンクターの型を非表示にします。サンプルを機能させるには、テンプレート化されたコンストラクターが必要です。

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

したがって、これをクラスBaseおよびDerivedで使用する場合...

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

... Y = Derivedのテンプレート化されたコンストラクターは、shared_ptrオブジェクトを構築するために使用されます。したがって、コンストラクターは、適切な削除オブジェクトと参照カウンターを作成し、この制御ブロックへのポインターをデータメンバーとして格納する機会があります。参照カウンタがゼロに達すると、以前に作成されたDerived-awaredeleterを使用してオブジェクトが破棄されます。

C ++ 11標準には、このコンストラクター(20.7.2.2.1)について次のように書かれています。

必要なもの: pに変換可能である必要がありますT*Y完全なタイプでなければなりません。表現delete pは適切に形成され、明確に定義された動作を持ち、例外をスローしてはなりません。

効果:構築shared_ptr所有オブジェクトますポインタp

そして、デストラクタ(20.7.2.2.2)の場合:

効果:もしが*thisある空の、または別と共有の所有権shared_ptrインスタンス(use_count() > 1)、副作用がありません。それ以外の場合、*thisオブジェクトpと削除機能を所有している場合はdd(p)が呼び出されます。 それ以外の場合、*thisポインタを所有している場合p、およびdelete p呼ばれています。

(太字フォントを使用することを強調するのは私のものです)。


the upcoming standard also requires this behaviour:(a)どの規格および(b)(規格への)参照を提供できますか?
kevinarpe 2016年

に十分なポイントがないので、@ sellibitzeの回答にコメントを追加したいだけですadd a comment。IMO、それ以上Boost does thisですthe Standard requires。私が理解していることから、規格がそれを要求しているとは思いません。@sellibitzeの例の話をshared_ptr<Base> sp (new Derived);必要constructorためだけに頼むdelete Derived明確に定義され、よく形成されています。仕様に関してはdestructor、そこにもあるpが、私はそれが参照するとは思わないpの仕様にconstructor
LujunWeng19年

28

shared_ptrが作成されると、削除機能が格納されますそれ自体の中にオブジェクトがます。このオブジェクトは、shared_ptrが指定されたリソースを解放しようとしているときに呼び出されます。構築の時点でリソースを破棄する方法を知っているので、不完全な型でshared_ptrを使用できます。shared_ptrを作成した人は誰でも、そこに正しい削除プログラムを保存しました。

たとえば、カスタム削除機能を作成できます。

void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

pはDeleteDerivedを呼び出して、ポイントされたオブジェクトを破棄します。実装はこれを自動的に行います。


4
不完全な型についてのコメントは+1で、shared_ptr属性としてaを使用する場合に非常に便利です。
Matthieu M.

16

単に、

shared_ptr Baseのデストラクタではなく、指定されたオブジェクトのデストラクタを常に使用するコンストラクタによって作成される特別な削除関数を使用します。これはテンプレートメタプログラミングで少し作業しますが、機能します。

そんな感じ

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

1
うーん...興味深い、私はこれを信じ始めています:)
Armen Tsirunyan 2010年

1
@Armen Tsirunyanディスカッションを開始する前に、shared_ptrの設計の説明を確認しておく必要があります。この「デリータのキャプチャが」のshared_ptrの本質的な特徴の一つである...
ポールMichalik

6
@ paul_71:私はあなたに同意します。一方で、この議論は私だけでなく、shared_ptrについてこの事実を知らなかった他の人々にとっても有益だったと思います。とにかくこのスレッドを開始することは大きな罪ではなかったと思います:)
Armen Tsirunyan 2010年

3
@Armenもちろん違います。むしろ、経験豊富なc ++開発者でも頻繁に監視されるshared_ptr <T>のこの非常に重要な機能を指摘するのに良い仕事をしました。
Paul Michalik 2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.