クラスのデストラクタで、他の場所で削除されたメンバーの 1 つを除いて、オブジェクト全体を削除したいと思います。まず、これはまったく理不尽ですか?そうでないと仮定すると、どうすればこれを行うことができますか?空のボディでデストラクタを作成すると、すべてのメンバーが削除されるのを防ぐことができると思いました(デストラクタは何もしないため)が、そうではないようです。
12 に答える
簡単な答え: ありません。
より長い答え:「メンバー」が実際に他の割り当てへのポインターである場合、他の割り当てを削除しないように手配できます。
しかし、通常、コンストラクターで他のブロックを割り当てた場合は、デストラクターでそれを削除します。それ以外の場合は、問題のブロックの「所有権」を慎重に処理する必要があります。普通の c でのメモリ管理によく似ています。可能ですが、危険を伴います。
幸運を。
「削除」の意味によって異なります。それらがスマート ポインターになく、明示的に削除されていない場合、それらは削除されません。クラスの一部であるメンバー:
class Bar {
//...
private:
Foo foo;
};
デストラクタによって削除されず (動的に割り当てられていないため)、単に破棄されます。それらはクラス内に「住んでいる」ため、破壊されると消えてしまいます。
2 つの場所の間で共有の「所有権」を探している場合、必要なのは動的に割り当てられた shared_ptr です。
#include <memory>
class Bar {
// ...
private:
std::tr1::shared_ptr<Foo> foo;
};
メンバーが (ポインターや参照ではなく)値によって含まれている場合、そのメンバーが削除されるのを防ぐことはできません。
代わりに他の場所で削除する場合は、ポインターまたは参照によって含まれるようにします。
class House
{
Door door; //contained by value, will be destroyed when the House is
}
class House
{
Door& door; //contained by reference, will not be destroyed when the House is
}
デストラクタのコードは、動的に割り当てられたメンバーを削除するだけです。メンバーの破棄はオプションではありません。以前に明示的に割り当てたものの割り当て解除のみを制御できます (演算子 new を使用)。
やりたいことは、クラスと外部コードの両方が同じ外部オブジェクトへのポインターを共有する shared_ptr を使用して取得できます。このようにして、そのオブジェクトへのすべてのポインターがスコープ外になった場合にのみ、そのオブジェクトが削除されます。ただし、循環参照を行わないように注意してください。shared_ptr には「ガベージ コレクター」の知恵がありません。
もちろん、これらの場所で共有される通常のポインターを使用することもできますが、これはほとんどの場合、悪い考えであり、後で適切なリソースの割り当て解除について頭痛の種になる傾向があります。
まず第一に、メンバー オブジェクトが値によって含まれている場合、コンテナー オブジェクトが破棄されたときに単純にスコープ外になり、自動的に割り当てが解除されるのを防ぐことはできません。
代わりに、コンテナー オブジェクトによって間接的に参照されている場合 (ポインターなどを使用)、削除しないようにするために特に何もする必要はありません。明示的にコードを記述しない限り、デストラクタは何も削除しません。
これが不当であるかどうかという質問については、一般的にはそうではないと思いますが、問題のメンバーを所有するオブジェクトは何かを明確にする必要があります (通常、ドキュメントでは、C++ にはこの概念に対する言語サポートがないため)。 .
ほとんどの場合、同じアクションでオブジェクト全体を破壊しないと問題が発生すると思います。クラスには、デストラクタ内で呼び出されるそのメンバーのクリーンアップ メソッドが必要なようです。何らかの理由でメンバーをより早く破棄する必要がある場合、メソッドは早期に戻ることができます。
デストラクタで削除されるクラスメンバーについて話すときは、ポインタではないメンバーとポインタであるメンバーを区別する必要があります。次のようなクラスがあるとします。
class Foo
{
public:
Foo() {p = new int;}
~Foo(){}
private:
int a;
int *p;
};
このクラスには、整数と整数a
へのポインタの2つのデータメンバーがありますp
。デストラクタが呼び出されると、オブジェクトが破棄されます。つまり、そのすべてのメンバーのデストラクタが呼び出されます。これは、デストラクタのボディが空の場合でも発生します。整数のようなプリミティブ型の場合、そのデストラクタを呼び出すことは、それが占有しているメモリが解放されることを意味します。ただし、ポインタを破棄すると問題が発生します。ポインタが指すものはすべて、デフォルトでは破棄されません。そのためには、明示的にを呼び出す必要がありますdelete
。
したがって、この例でa
は、デストラクタが呼び出されたときに破棄されます。また、破棄されますが、ポイントするp
ものは何もありません。p
ポイントするメモリを解放したい場合p
、のデストラクタは次のFoo
ようになります。
~Foo() {delete p};
したがって、質問に戻ると、オブジェクトのデストラクタが呼び出されたときに、ポインタではないクラスのすべてのメンバーが何があっても破棄されます。一方、ポインタであるメンバーがある場合、デストラクタで特にdeleteを呼び出さない限り、それらが指すものはすべて破棄されません。
不合理ではありませんが、管理対象リソースのクリーンアップが暗黙的に処理されるように注意する必要があります。
(人々が一般的に心配する最初の管理対象リソースはメモリですが、リークする可能性のあるもの(メモリ、ファイルハンドル、IDispatchポインタ)には、クリーンアップを暗黙的に処理するコードが必要です)。
複数のオブジェクトによって共有される管理対象リソースの場合(「このオブジェクト」が「そのオブジェクト」によってクリーンアップされるものへのポインターを持っていると想定される場合はほぼ確実に)、通常、管理するために「参照カウントポインター」のいずれかが必要です。ライフタイム要件に応じて、オブジェクトまたは「弱いポインタ」。
共有されていない管理対象リソース(特に、例外がスローされる可能性がある場合に適切に管理する必要があるリソース)の場合は、auto_ptrまたはその他のバリアントの方が適している場合があります。
スコットマイヤーズの効果的なC++の本は、スマートポインターについて学ぶための合理的な出発点でしたが、実際には、Boostのような精査されたライブラリを入手して、他の誰かにあいまいなコーナーケースの取得について心配させる必要があります(コンストラクターが例外?)そうです。
弱いポインターと強いポインターについて誰も言及しなかったのはなぜですか?
ストロング ポインターは、正常に動作するスマート ポインターです。
ウィーク ポインターは、すべてのストロング ポインターがスコープ外にない限り、自分自身を削除できないスマート ポインターです。
強いポインタは所有権を示し、弱いポインタは共有を示します。
実装については、boost.shared_ptrとboost.weak_ptrおよびLoki の StrongPtrを参照してください。RAII
も見てください。RAII を知っていれば、この質問に対する答えを自分で知っていたでしょう。
これは可能ですが、基本的に@dmckeeが言ったように、それは所有権の問題です。その場合は、参照カウントに行くことができます。すなわち
class A
{
RefObj* obj;
A()
{
obj = new RefObj;
}
~A()
{
obj->ReleaseRef();
}
}
RefObj
{
int m_iRefCounter;
RefObj()
{
m_iRefCounter = 1;
}
AddRef()
{
m_iRefCounter++;
}
ReleaseRef()
{
m_iRefCounter--
if(m_iRefCounter == 0)
{
delete this;
}
}
}
}
まず、これはまったく理不尽ですか?
私は不合理だとは言いませんが、おそらく疑わしいです。
あるクラスが所有することは完全に有効であるため、クリーンアップを処理する必要がありますが、同時に別のクラスでそのオブジェクトへの参照またはポインターを保持する必要があります。
ただし、2 番目のクラス reall がそのポインターを持つべきかどうかは疑問かもしれません。たとえば、親クラスやリソース マネージャーを呼び出して、必要なときに常に get メソッドを使用してそのポインターを取得することをお勧めします。
このメンバーに動的にメモリを割り当てた場合、オブジェクトを破棄する前にこのメンバーへの参照を共有し、メンバーがオブジェクトのデストラクタで破棄されないようにすれば可能です。しかし、この慣行はあまり合理的ではないと思います。