C++仕様では、デフォルトのデストラクタがすべての非静的メンバーを削除するとしています。それにもかかわらず、私はそれを達成することができません。
私はこれを持っています:
class N {
public:
~N() {
std::cout << "Destroying object of type N";
}
};
class M {
public:
M() {
n = new N;
}
// ~M() { //this should happen by default
// delete n;
// }
private:
N* n;
};
次に、これは指定されたメッセージを出力するはずですが、そうではありません:
M* m = new M();
delete m; //this should invoke the default destructor
n
が指すオブジェクトをデフォルトで削除する必要があると思う理由は何ですか?デフォルトのデストラクタは、ポインタが指しているものではなく、ポインタを破棄します。
編集:これをもう少し明確にできるかどうかを確認します。
ローカルポインタがあり、それがスコープ外になった場合、それが指すオブジェクトが破棄されると思いますか?
{
Thing* t = new Thing;
// do some stuff here
// no "delete t;"
}
t
ポインターはクリーンアップされますが、それが指すThing
はクリーンアップされません。これはリークです。本質的に同じことがあなたのクラスでも起こっています。
次のようなものを想像してみてください。
class M {
public:
M() { }
// ~M() { // If this happens by default
// delete n; // then this will delete an arbitrary pointer!
// }
private:
N* n;
};
あなたはC++のポインタを自分で使っています。誰もあなたのためにそれらを自動的に削除することはありません。
デフォルトのデストラクタは、実際にすべてのメンバーオブジェクトを破棄します。ただし、この場合のメンバーオブジェクトはポインタそのものであり、それが指すものではありません。これはあなたを混乱させたかもしれません。
ただし、単純な組み込みポインターの代わりに スマートポインター を使用する場合、そのような「ポインター」(実際にはクラス)を破棄すると、ポイントされたオブジェクトが破棄される可能性があります。 。
デフォルトのデストラクタはポインタを破壊しています。 N
のデフォルトのデストラクタを使用してM
を削除する場合は、スマートポインタを使用してください。 _N * n;
_を_auto_ptr<N> n;
_に変更すると、n
が破棄されます。
編集:コメントで指摘されているように、_auto_ptr<>
_はすべての用途に最適なスマートポインターではありませんが、ここで求められているように見えます。これは具体的に所有権を表します。MのNは、Mの期間中存在し、もはや存在しません。 _auto_ptr<>
_をコピーまたは割り当てることは、所有権の変更を表しますが、これは通常、必要なものではありません。 Mからポインタを渡したい場合は、n.get()
から取得した_N *
_を渡す必要があります。
より一般的な解決策は_boost::shared_ptr<>
_で、これはC++ 0x標準になります。これは、生のポインターが使用される場所であればどこでも使用できます。これは最も効率的な構成ではなく、循環参照に問題がありますが、一般的には安全な構成です。
別の編集:別のコメントの質問に答えるために、デフォルトのデストラクタの標準的な動作は、すべてのデータメンバーと基本クラスを破棄することです。ただし、生のポインターを削除すると、ポインターが指すものではなく、単にポインターが削除されます。結局のところ、実装はそれが唯一のポインタなのか、重要なポインタなのか、あるいはそのようなものなのかを知ることができません。スマートポインタの背後にある考え方は、スマートポインタを削除すると、少なくともそれが指すものが削除されるということです。これは通常、望ましい動作です。
ポイントされたオブジェクトが含まれているオブジェクトに属しているように見えるときにポインタを使用する理由はありますか?オブジェクトを値で格納するだけです。
class M
{
N n;
public:
M() : n()
{
}
};
デストラクタdeletesメンバーと言うのは誤りです。各メンバー(および基本クラス)のデストラクタを呼び出します。これは、組み込み型(ポインターなど)の場合、何もしないことを意味します。
マッチング 新着と 削除sはあなたの責任です(手動で、またはスマートポインタの助けを借りて)。
あなたの議論は正しいように見えるかもしれませんが、それは物事がポインタに対してどのように機能するかではありません。
n
は実際に破棄されていますが、これはN*
デストラクタが呼び出されていることを意味し、n
が指しているオブジェクトは破棄されません。 N*
のデストラクタをint
のデストラクタであるかのように考えてください。値を削除します。ポインタについても同じことが起こり、ポイントしているアドレスも削除されますが、削除したばかりのアドレスにあるオブジェクトを削除する必要はありません。
ここでの間接参照のレベルについて混乱しているかもしれません。インスタンスが破棄されると、各データメンバーは実際にインスタンスとともに破棄されます。あなたの場合、M
が破棄されてM::~M()
が呼び出されると、その変数n
は実際に破棄されます。問題は、n
が_N *
_であるため、ポインターが破棄されても、ポインターが指すものは破棄されないことです。
delete
はこのようには機能しません。あなたの簡単なステートメントを考えてみましょう:
_delete n;
_
上記のステートメントは、n
が指すものを破棄します。これはタイプN
のオブジェクトです。 _N *
_ポインタであるn
自体は破棄されません。
M::~M()
が_delete n;
_を自動的に呼び出さないのには非常に理由があります。これは次のとおりです。参照されるN
オブジェクトは複数のM
オブジェクト間で共有される可能性があります。そして、1つのM
が破壊された場合、残りは彼らが指していたN
を失い、どこにでも恐ろしいダングリングポインタが残ります。 C++は、ポインタを使って意味することを解釈しようとはせず、言われた行うことを行うだけです。
要するに、M
は、破壊されたときに実際にすべてのメンバーを破壊しているのです。この破壊は、あなたが思っていることを実行しないというだけです。オブジェクトの所有権を取得し、ポインターが破棄されたときにオブジェクトを破棄するポインター型が必要な場合は、_std::auto_ptr
_を参照してください。
デフォルトのデストラクタは次のようになります。
~M()
{
}
デフォルトのデストラクタは、ポイントされたものを処理するためのコードを挿入しません。 nスタック変数を指している場合はどうなりますか? delete nを自動的に挿入するとクラッシュします。
デフォルトのデストラクタは、クラスの各メンバーでデストラクタを呼び出します(member。〜T())。ポインタの場合、myint。〜int()が何もしないのと同じように、これは何もしません(何もしません)が、デストラクタが定義されているメンバークラスの場合、デストラクタが呼び出されます。
別の例を次に示します。
struct MyClass {
public:
MyClass() { .. } // doesn't matter what this does
int x;
int* p;
std::string s;
std::vector<int> v;
};
実際のデフォルトのデストラクタはこれを行っています:
MyClass::~MyClass()
{
// Call destructor on member variables in reverse order
v.~std::vector<int>(); // frees memory
s.~std::string(); // frees memory
p.~int*(); // does nothing, no custom destructor
x.~int(); // does nothing, no custom destructor
}
もちろん、デストラクタを定義すると、デストラクタのコードが実行されますbeforeメンバー変数は破棄されます(明らかに、そうでない場合は無効になります!)。
ポインタの使用は避けてください。それらは最後の手段の要素です。
class N {
public:
~N() {
std::cout << "Destroying object of type N";
}
};
class M {
public:
M() {
// n = new N; no need, default constructor by default
}
// ~M() { //this should happen by default
// delete n;
// }
private:
N n; // No pointer here
};
次に、このように使用します
main(int, char**)
{
M m;
}
これにより、タイプNの破壊オブジェクトが表示されます。
Mデストラクタには「deleten」が必要です。
new
を使用してインスタンスを作成しているため、デフォルトでは削除されません。
私はあなたが非常に単純な例から利益を得ることができると思います:
_int main(int argc, char* argv[])
{
N* n = new N();
} // n is destructed here
_
これも何も印刷しません。
どうして ? pointer
(n
)は破壊されているため、_*n
_を指すオブジェクトではありません。
もちろん、あなたはそれが指し示したオブジェクトを破壊することを望まないでしょう:
_int main(int argc, char* argv[])
{
N myObject;
{
N* n = &myObject;
} // n is destructed here, myObject is not
myObject.foo();
} // myObject is destructed here
_
_C#
_やJava
のような言語とは異なり、C++でオブジェクトを作成する方法は2つあります。直接_N myObject
_(スタック上)またはnew
のようにnew N()
の場合、オブジェクトはヒープに配置され、後で解放する責任があります。
したがって、デストラクタはポインタを破棄しますが、ポイントされたオブジェクトは破棄しません。新規なしで(そしてポインタを使用せずに)オブジェクトを割り当てるか、自動にしたい場合は_Smart Pointer
_を使用します。
class N {
public:
~N() {
std::cout << "Destroying object of type N";
}
};
class M {
public:
M() {
n = new N;
}
// ~M() { //this should happen by default
// delete n;
// }
private:
N* n;
};
そして今、期待は:
M* m = new M();
delete m; //this should invoke the default destructor
クラスMがNから派生している場合にのみ発生します。
class M: Class N {
...
この状況でのみ、
M* m = new M()
nのコンストラクターを呼び出し、次にMのコンストラクターを呼び出します。
delete m;
最初にMのデストラクタを呼び出し、次にNを自動的に呼び出します