2017-03-28 16 views
0

私はそれを期待していない場合に子のデストラクタを呼び出すC++のシナリオを持っていました。最小限の再現率は以下のとおりです。デフォルトコンストラクタを使用する親クラス。子クラスのデストラクタが予期せず呼び出されました

#include <cstdio> 
#include <memory> 

using namespace std; 

class Parent { 
public: 
}; 

class Child : public Parent { 
    public: 
    ~Child() { 
     printf("Got here\n"); 
    } 
}; 

int 
main() 
{ 
    shared_ptr<Parent> x(new Child); 
} 

通常、これはバグです。開発者は、子デストラクタが呼び出されることを意図しており、正しいアクションは親に空の仮想デストラクタを挿入することです。しかし、私のショックでは、G ++ 4.4.7(ええ、私はそれが古いと知っています)と3.4.2をコンパイルして、子デストラクタと呼ばれるようにコンパイルします。

これは標準に準拠していますか?

+0

@chris yeah私はそれをチェックしました。それはまったく役に立ちません。 –

+0

@ n.m。私が個人的にそれを必要としているとは言えませんが、あなたがそうしたときにはいいことです。 – chris

+0

@ n.m。仮想デストラクタ –

答えて

5

でも、shared_ptrに特別な魔法がない場合でも、非仮想的なデストラクタを持つ親ポインタによる入力は、(子デストラクタの呼び出しの)結果は間違いなく準拠します。

この場合、shared_ptrは、渡された元のオブジェクトのタイプを「記憶」し、子ポインタ(保存されたデリゲーターを介して)によって破棄します。

+0

ええと、保存されたデリターはテンプレートタイプを使用していません。 – NathanOliver

+0

いいえ、おおまかに 'deleter 'の 'std :: function'(またはそれに相当するもの)を保存します。' U'はコンストラクタのパラメータ型であり、 'shared_ptr'テンプレート型ではありません。 'shared_ptr 'などでも動作します。 –

0

ここでは固有のptrが壊れてしまいます。しかし、共有されたptrはここでいくつかの "魔法"をします。

shared_ptr<T> has two things they manage; the T * `と参照カウントブロック。

リファレンスカウントブロックには、2つのリファレンスが含まれています.1つは強参照用、もう1つは弱参照用、もう1つは型消去されたdeleterです。このstd::functionのようなタイプ消去されたデリーターは、あなたが所有するものを削除する方法をどのように覚えています。

U*uを指定してshared_ptrを作成すると、デフォルトではそのデリータに[u]{std::default_delete<U>{}(u);}が格納されます。実際に

、それは渡されたタイプに基づいて、オブジェクトを削除する方法を覚えている。

shared_ptr極めて柔軟です。あなたはデフォルトのものを置き換えるために、カスタム削除手段に渡すことができます

、あなたが保存されT*から使用参照カウントブロックを分割するエイリアシングコンストラクタを使用することができます、あなたは参照カウントブロックとでTを割り当てるためにmake_sharedを使用することができます同じメモリ割り当て。

参照カウントブロックのオーバーヘッドは、なぜそれがdeleterを格納するのかです。我々はブロックを必要としていたので、それほど高価ではないと見なされました。これと比較すると、デフォルトではunique_ptrにはそのようなことはありません。 Deleterを明示的に追加する必要があります。必要に応じて、shared_ptrがデフォルトで行うすべての巧妙なトリックを管理する必要があります。 unique_ptrは、生の所有ポインターに対して基本的にゼロオーバーヘッドを持ちます。 shared_ptrには顕著なオーバーヘッドがありますが、通常はメモリ割り当てオーバーヘッドに比べて小さいです。

関連する問題