C++ 言語ではメモリ管理がプログラマの手に委ねられているため、このレベルの混乱が見られることがあります。
Luchian Grigore が言ったことを繰り返しますが、記憶には主に 3 つのタイプがあります。
- 自動保管(スタック)
- 動的ストレージ (ヒープ)
- 静的ストレージ
オブジェクトを自動ストレージに割り当てた場合、オブジェクトはスコープが終了すると破棄されます。例えば
void foo() {
MyClass myclass_instance;
myclass_instance.doSomething();
}
上記の場合、関数が終了するmyclass_instance
と自動的に破棄されます。
代わりに でヒープにオブジェクトを割り当てる場合はnew
、 でデストラクタを呼び出すのはあなたの責任delete
です。
C++ でも、オブジェクトはサブオブジェクトを持つことができます。例えば:
class MyBiggerClass {
MyClass x1;
MyClass x2;
...
};
これらのサブオブジェクトは、包含オブジェクトが割り当てられているのと同じメモリに割り当てられます
void foo() {
MyBiggerClass big_instance;
MyBiggerClass *p = new MyBiggerClass();
...
delete p;
}
上記の場合、2 つのサブオブジェクトbig_instance.x1
とbig_instance.x2
は自動ストレージ (スタック) に割り当てられ、p->x1
とp->x2
はヒープに割り当てられます。
ただし、この場合、呼び出す必要はないことに注意してくださいdelete p->x1;
(コンパイルエラー、p->x1
ポインターではありません) またはdelete &(p->x1);
(構文的に有効ですが、ヒープに明示的に割り当てられたのではなく、別のサブオブジェクトとして割り当てられたため、論理的な間違いです)物体)。メインオブジェクトp
を削除するだけです。
別の複雑さは、オブジェクトが他のオブジェクトへのポインターを直接含めるのではなく保持する可能性があることです。
class MyOtherBigClass {
MyClass *px1;
MyClass *px2;
};
この場合MyOtherBigClass
、サブオブジェクトのメモリを見つける必要があるのはコンストラクタであり、サブオブジェクト~MyOtherBigClass
を破棄してメモリを解放する必要があるのはコンストラクタです。
C++ では、生のポインターを破棄してもコンテンツは自動的に破棄されません。
単純なケースの基本クラスは、隠し埋め込みサブオブジェクトと見なすことができます。つまり、ベース オブジェクトのインスタンスが派生オブジェクトに埋め込まれているようなものです。
class MyBaseClass {
...
};
class MyDerivedClass : MyBaseClass {
MyBaseClass __base__; // <== just for explanation of how it works: the base
// sub-object is already present, you don't
// need to declare it and it's a sub-object that
// has no name. In the C++ standard you can find
// this hidden sub-object referenced quite often.
...
};
これは、言語によって自動的に処理されるため、派生オブジェクトのデストラクタがベース オブジェクトのデストラクタを呼び出す必要がないことを意味します。仮想ベースの場合はより複雑ですが、ベース デストラクタの呼び出しは自動的に行われます。
メモリ管理がプログラマーの管理下にあることを考えると、常にオブジェクト リークや複数の破壊につながる複雑なコードの混乱をプログラマーが回避するのに役立ついくつかの戦略が登場しています。
インスタンスの有効期間をどのように処理するかを慎重に計画してください。後で修正することは不可能になるため、これを後付けとして残すことはできません。すべてのオブジェクト インスタンスについて、作成者と破棄者を明確にする必要があります。
オブジェクトをいつ破棄するかを事前に計画することが不可能な場合は、参照カウンターを使用します。すべてのオブジェクトについて、それを参照しているポインターの数を追跡し、この数がゼロに達したらオブジェクトを破棄します。これを処理できるスマート ポインターがあります。
すでに破棄されているオブジェクトへのポインターを保持しないでください。
含まれているオブジェクトの有効期間を処理するために明示的に設計されたクラスであるコンテナーを使用します。例はstd::vector
またはstd::map
です。