3

baseクラスとがありますderived。それで:

class base {
     protected:
          ~base(){
                //...
          }
     // ...
};

class derived : public base {
     // ...
};

そして今、スマートポインタクラスで上記のクラスを使用してこのコードがあるとしましょう:

SmartPointer<base> bptr(new derived());
delete bptr;

derivedのデストラクタを呼び出すことでオブジェクトのスライスを防ぐことができると理解していderivedますが、それをどのように知っているのでしょうか。スマートポインタに格納されている参照は、タイプの参照ではないでしょうbase*か。ある種の階層ツリーをトラバースし、そのポインタをにキャストしてからderived*、deleteを呼び出しますか?それとも私が知らないことが他にありますか?

実装はおそらくスレッドセーフで、邪魔にならず、参照カウントです。

はい、あなたが見るクラスは私がテストしているクラスに似ています。これらの与えられたクラスでこれを行う方法があるようです。上記の私の質問でどのように言及されているかについての主な考えは、そのような実装がどのように機能するかについてはわかりません。

4

5 に答える 5

7

まず第一に、このままではコードが機能しないということです。のデストラクタはbase少なくともprotected(または派生クラスがベースのフレンドである) 必要があります。privateデストラクタとは、コンパイラが派生クラスのデストラクタを記述することを許可しないことを意味します。protectedここで、デストラクタがあると仮定します... (拡張するクラスを設計する場合は、パブリック仮想デストラクタまたは保護された非仮想デストラクタのいずれかを提供してください!)

すべては の実装に依存し、SmartPointer特にstd::shared_ptr(またはブースト対応のboost::shared_ptr) はその状況をきれいに管理できます。このソリューションは、破壊目的で型のある種の部分的な型消去を実行します。基本的に、スマート ポインターには、ポインターに割り当てることができる任意のポインターを受け入れるテンプレート化されたコンストラクターがありますが、テンプレート化されているbaseため、具体的な型を認識しています。その時点でdeleter、適切なデストラクタを呼び出す合成関数が格納されます。

簡単にするために、次を使用しstd::functionます。

template <typename T>
void delete_deleter( void * p ) {
   delete static_cast<T*>(p);
}

template <typename T>
class shared_pointer {
    T * ptr;
    std::function<void(void*)> deleter;
public:
    template <typename U>
    shared_pointer( U* p, std::function<void()> d = delete_deleter<U> ) 
       : ptr(p), deleter(d)
    {}
    ~shared_pointer() {
       deleter( ptr );  // call the stored destructor
    }
};

コードは展示専用です。本番用に微調整する必要があります(function参照カウントを保存する場所...)が、アイデアを与えるには十分です:オブジェクトの正確なタイプが知られている場合 (スマート ポインターを作成するとき)、必要なデストラクタの正確なバージョンを呼び出すラッパーを作成し (タイプ消去の一部を提供します)、それをそのままにしてdelete、オブジェクトが必要なときに代わりにそれを呼び出します。deleteオペレーター。

これは、代わりに特別なメソッドを呼び出す必要がある他のリソースを管理するためにも使用できますdelete

// exhibition only!
shared_pointer<Foo> p( Factory.create(), &Factory::release );

繰り返しますが、このプロダクションの準備を整える前に、かなり多くの作業が必要です。

消去を簡素化するために使用される依存関係std::functionは、問題から排除できます。単純なケース (スマート ポインターで割り当てられたメモリnewと解放されたメモリのみdeleteがサポートされます) では、deleter基本クラスに単一の virtual を提供し、既存のクラスを現在の実装でそのオーバーライドからテンプレート化された派生クラスにoperator()(void*)リファクタリングします。一般的なケース (任意のタイプのリソースを保持する) を使用する必要がある場合は、努力する価値はありません。単にまたはを使用してください。delete_deleterdeleteroperator()(void*)std::functionboost::function

于 2011-04-26T07:55:33.897 に答える
2

まず第一に、デストラクタはプライベートであってはなりません。そうしないと、コンパイルされません。次に、「スマートポインター」を使用している場合は、ポインターを手動で削除するべきではありません(使用している実装はわかりませんが、これは奇妙なことです)。

とにかく、オブジェクトが基本クラスへのポインタを介して削除されたときに派生クラスのデストラクタがどのように呼び出されるのか知りたい場合は、答えはポリモーフィズムです。しかしvirtual、デストラクタからの宣言がありません。現在、コードは派生クラスのデストラクタを呼び出しません。

ほとんどのC++実装がこれを実装する方法は、仮想テーブルを介して行われます。

于 2011-04-26T07:14:42.613 に答える
1

ブースト スマート ポインターまたはクラス以外のものを使用している場合、このコードはコンパイルされません。これは、クラスのデストラクタfriendが(他の独立したクラスの場合と同じ) であるためです。BaseBaseprotectedprivateBase

SmartPointer<Base>では、あなたが と友達になったとしましょうBase。この場合、コードは機能しますが、クラスは多態的ではないため、Derivedのデストラクタではなく、のデストラクタを呼び出します。destrucotr のasを宣言する必要があります。最後のケースでは、スマート ポインターが削除されたときに正しいデストラクタが呼び出されます。BaseBaseBasevirtual

于 2011-04-26T07:13:01.357 に答える
0

このプログラムは無効です。

1)ベースのdtorはプライベートです

2)ベースのdtorは仮想ではありません

あなたの質問に答えるために:あなたは#1と#2を修正する必要があります。次に、動的ディスパッチを使用してdtorが呼び出されます(これにより、構築の逆の順序で各dtorが呼び出されます)。

これらの修正を行わずにSmartPointer、この例で派生のdtorを呼び出すことを知る唯一の方法は、定義された方法で、SmartPointer過度に賢い(または使用するのが面倒な)場合です。

于 2011-04-26T07:14:11.177 に答える
0

ベースポインタを介して削除するときに派生クラスのデストラクタが呼び出されるようにするには、基本クラスの記述子が仮想である必要があります。

仮想記述子に関するウィキペディアのエントリ

于 2011-04-26T07:15:01.297 に答える