stl の vector のようなコンテナ オブジェクトは、ヒープで作成されているにもかかわらず、どのように破棄されますか?
編集
コンテナーがポインターを保持している場合、それらのポインター オブジェクトを破棄する方法
stl の vector のようなコンテナ オブジェクトは、ヒープで作成されているにもかかわらず、どのように破棄されますか?
編集
コンテナーがポインターを保持している場合、それらのポインター オブジェクトを破棄する方法
ポインタのSTLコンテナは、ポイントされたデータをクリーンアップしません。ポインタを保持しているスペースのみをクリーンアップします。ベクターでポインターデータをクリーンアップする場合は、ある種のスマートポインター実装を使用する必要があります。
{
std::vector<SomeClass*> v1;
v1.push_back(new SomeClass());
std::vector<boost::shared_ptr<SomeClass> > v2;
boost::shared_ptr<SomeClass> obj(new SomeClass);
v2.push_back(obj);
}
そのスコープが終了すると、両方のベクトルが内部配列を解放します。v1は、それへのポインタのみが配列にあるため、作成されたSomeClassをリークします。v2はデータをリークしません。
がある場合はvector<T*>
、ベクトルを削除する前にコードでこれらのポインタを削除する必要があります。そうしないと、そのメモリがリークされます。
C ++はガベージコレクションを行わないことを知ってください。これが理由の例です(構文エラーの謝罪、C ++を作成してからしばらく経ちました):
typedef vector<T*> vt;
⋮
vt *vt1 = new vt, *vt2 = new vt;
T* t = new T;
vt1.push_back(t);
vt2.push_back(t);
⋮
delete vt1;
最後の行(delete vt1;
)は、そこに含まれるポインタを明らかに削除してはなりません。結局のところ、それはvt2にもあります。ですから、そうではありません。また、の削除も行われませんvt2
。
(destroyでポインターを削除するベクトル型が必要な場合は、もちろんそのような型を書き込むことができます。おそらくそうなっています。ただし、他の誰かがまだコピーを保持しているポインターを削除することに注意してください。)
ベクトルがスコープ外になると、コンパイラはデストラクタへの呼び出しを発行し、ヒープに割り当てられたメモリを解放します。
これはやや誤称です。ベクターは、ほとんどの STL コンテナーと同様に、2 つの論理部分で構成されます。
#2 は構成可能ですが、ほぼ常にヒープ上に存在します。#1ただし、スタックまたはヒープのいずれかに存在できます。それは、割り当て方法に依存します。例えば
void foo() {
vector<int> v;
v.push_back(42);
}
この場合、パート #1 はスタック上にあります。
では、#2 はどのように破壊されるのでしょうか? ベクトルの最初の部分が破棄されると、2 番目の部分も同様に破棄されます。これは、ベクター クラスのデストラクタ内の基になる配列を削除することによって行われます。
ポインターを STL コンテナー クラスに格納する場合は、オブジェクトが破棄される前にそれらを手動で削除する必要があります。これは、コンテナー全体をループして各項目を削除するか、ある種のスマート ポインター クラスを使用することで実行できます。ただし、コンテナーではまったく機能しないため、auto_ptr は使用しないでください。
これの良い副作用は、プログラム内にポインターの複数のコンテナーを保持できますが、それらのオブジェクトはそれらのコンテナーの 1 つによって所有されるだけであり、その 1 つのコンテナーのみをクリーンアップする必要があることです。
ポインターを削除する最も簡単な方法は次のとおりです。
for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
delete (*it);
}
ベクトル内でスマート ポインターを使用するか、boost の ptr_vector を使用します。その中に割り当てられたオブジェクトを自動的に解放します。マップやセットなどもあります。
http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_vector.html およびメイン サイト: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/ doc/ptr_container.html
最初の質問に答えるには:
STLクラスについて特別なことは何もありません(私は願っています)。これらは、他のテンプレートクラスとまったく同じように機能します。したがって、C ++にはガベージコレクションがないため、ヒープに割り当てられた場合、それらは自動的に破棄されません(いくつかの派手なautoptrビジネスなどで指示しない限り)。スタックに(新規なしで)割り当てると、ほとんどの場合、C++によって自動的に管理されます。
2番目の質問として、C++での一般的なメモリ管理の基本を示す非常に単純なArrayOfTenクラスを次に示します。
/* Holds ten Objects. */
class ArrayOfTen {
public:
ArrayOfTen() {
m_data = new Object[10];
}
~ArrayOfTen() {
delete[] m_data;
}
Object &operator[](int index) {
/* TODO Range checking */
return m_data[index];
}
private:
Object *m_data;
ArrayOfTen &operator=(const ArrayOfTen &) { }
};
ArrayOfTen myArray;
myArray[0] = Object("hello world"); // bleh
基本的に、ArrayOfTenクラスは、ヒープ上に10個のObject要素の内部配列を保持します。コンストラクターでnew[]が呼び出されると、10個のオブジェクト用のスペースがヒープに割り当てられ、10個のオブジェクトが作成されます。同様に、デストラクタでdelete []が呼び出されると、10個のオブジェクトが分解され、以前に割り当てられたメモリが解放されます。
ほとんどの(すべて?)STLタイプの場合、サイズ変更はバックグラウンドで行われ、要素に合うように十分なメモリが確保されていることを確認します。上記のクラスは、10個のオブジェクトの配列のみをサポートします。これは基本的に、Object[10]の非常に限定的なtypedefです。
標準のSTLコンテナーは、コピーコンストラクターを使用して、元のオブジェクトのコピーをコンテナーに配置します。コンテナが破棄されると、コンテナ内の各オブジェクトのデストラクタも呼び出され、オブジェクトを安全に破棄します。
ポインタは同じように処理されます。
ポインタはPODデータです。ポインタのコピーコンストラクタはアドレスをコピーするためのものであり、PODデータにはデストラクタがありません。コンテナでポインタを管理する場合は、次のことを行う必要があります。
私はポインターコンテナーを好みます:
ポインターコンテナーは、ポインターを入れることを除いてSTLコンテナーと同じですが、コンテナーはポインターが指すオブジェクトの所有権を取得し、オブジェクトの割り当てを解除します(通常はdeleteを呼び出します)。コンテナが破壊されました。
ptrコンテナのメンバーにアクセスすると、それらは参照を介して返されるため、標準アルゴリズムで使用するための標準コンテナと同じように動作します。
int main()
{
boost::ptr_vector<int> data;
data.push_back(new int(5));
data.push_back(new int(6));
std::cout << data[0] << "\n"; // Prints 5.
std::cout << data[1] << "\n"; // Prints 6.
} // data deallocated.
// This will also de-allocate all pointers that it contains.
// by calling delete on the pointers. Therefore this will not leak.
コンテナ内のスマートポインタは有効な代替手段であるということも指摘しておく必要があります。残念ながら、std ::auto_ptr<>はこの状況でのスマートポインタの有効な選択ではありません。
これは、STLコンテナが、コンテナに含まれるオブジェクトがコピー可能であると想定しているためです。残念ながら、std :: auto_ptr <>は、コピー時に元の値を破棄するため、コピーのソースをconstにできないため、従来の意味ではコピーできません。
ヒープ内の他のオブジェクトと同様に、手動で (delete を使用して) 破棄する必要があります。
STL コンテナーは他のオブジェクトと同様に、インスタンス化するとスタック上に作成されます。
std::vector<int> vec(10);
他のスタック変数と同様に、それが定義されている関数のスコープ内にのみ存在し、手動で削除する必要はありません。STL コンテナのデストラクタは、コンテナ内のすべての要素のデストラクタを呼び出します。
コンテナー内にポインターを保持することは危険な問題です。ポインターにはデストラクタがないため、生のポインターを STL コンテナーに入れたいとは思わないでしょう。例外セーフな方法でこれを行うのは非常に困難です。コードに try{}finally{} ブロックを散らかして、含まれているポインターが常に割り当て解除されるようにする必要があります。
では、生のポインターの代わりに何をコンテナーに入れる必要があるのでしょうか? boost::shared_ptr を起動するための +1 jmucchiello。boost::shared_ptr は STL コンテナーで安全に使用できます (std::auto_ptr とは異なります)。単純な参照カウント メカニズムを使用し、サイクルを含まないデータ構造に対して安全に使用できます。
サイクルを含むデータ構造には何が必要ですか? その場合、Java のような別の言語を使用することを本質的に意味するガベージ コレクションに卒業したいと思うでしょう。しかし、それは別の議論です。;)
ポイントされた要素を削除するために、単純なファンクターを作成しました。
template<typename T>
struct Delete {
void operator()( T* p ) const { delete p; }
};
std::vector< MyType > v;
// ....
std::for_each( v.begin(), v.end(), Delete<MyType>() );
ただし、ベクトルの内容が ... うーん... 共有される場合は、共有ポインターにフォールバックする必要があります。はい。