クラスからオブジェクトを返すとき、メモリを解放する適切なタイミングはいつですか?
例、
class AnimalLister
{
public:
Animal* getNewAnimal()
{
Animal* animal1 = new Animal();
return animal1;
}
}
Animal Listerのインスタンスを作成し、そこから動物の参照を取得した場合、どこで削除する必要がありますか?
int main() {
AnimalLister al;
Animal *a1, *a2;
a1 = al.getNewAnimal();
a2 = al.getNewAnimal();
}
ここでの問題は、AnimalListerに作成された動物のリストを追跡する方法がないため、作成されたオブジェクトを削除する方法を持つようにそのようなコードのロジックを変更するにはどうすればよいですか。
使用法に応じて、ここで使用できるオプションがいくつかあります。
動物を作成するたびにコピーを作成します。
class AnimalLister
{
public:
Animal getNewAnimal()
{
return Animal();
}
};
int main() {
AnimalLister al;
Animal a1 = al.getNewAnimal();
Animal a2 = al.getNewAnimal();
}
長所:
短所:
shared_ptr<Animal>
を返します:
class AnimalLister
{
public:
shared_ptr<Animal> getNewAnimal()
{
return new Animal();
}
};
int main() {
AnimalLister al;
shared_ptr<Animal> a1 = al.getNewAnimal();
shared_ptr<Animal> a2 = al.getNewAnimal();
}
長所:
Animal
は必要ありません。短所:
Animal
内のすべてのAnimalLister
割り当てを追跡します
class AnimalLister
{
vector<Animal *> Animals;
public:
Animal *getNewAnimal()
{
Animals.Push_back(NULL);
Animals.back() = new Animal();
return Animals.back();
}
~AnimalLister()
{
for(vector<Animal *>::iterator iAnimal = Animals.begin(); iAnimal != Animals.end(); ++iAnimal)
delete *iAnimal;
}
};
int main() {
AnimalLister al;
Animal *a1 = al.getNewAnimal();
Animal *a2 = al.getNewAnimal();
} // All the animals get deleted when al goes out of scope.
長所:
Animal
sが必要で、それらすべてを一度にリリースすることを計画している状況に最適です。Animal
を単一のdelete
で解放します。Animal
は必要ありません。短所:
生のポインタの代わりにstd::tr1::shared_ptr
(またはC++実装にTR1がない場合はboost::shared_ptr
)を返すことをお勧めします。したがって、Animal*
を使用する代わりに、代わりにstd::tr1::shared_ptr<Animal>
を使用してください。
共有ポインタは参照追跡を処理し、参照が残っていない場合はオブジェクトを自動的に削除します。
最も簡単な方法は、通常のポインターの代わりにスマートポインターを返すことです。例えば:
std::auto_ptr< Animal> getNewAnimal()
{
std::auto_ptr< Animal > animal1( new Animal() );
return animal1;
}
TR1またはBoostを使用できる場合は、shared_ptr <>を使用することもできます。
ポインタと割り当てられたメモリに関する古典的な問題の一種。それは責任についてです-AnimalListerオブジェクトによって割り当てられたメモリをクリーンアップする責任は誰にありますか。
割り当てられた各動物へのポインタをAnimalLister自体に格納し、クリーンアップすることができます。
ただし、main()にある動物へのポインタがいくつかあり、削除されたメモリを参照しています。
参照カウントソリューションが独自のソリューションをロールするよりもうまく機能すると思う理由の1つ。
動物ポインタの削除が必要であることを明確にする「freeAnimal(Animal *)」メソッドを実装します。
別の方法は、動物オブジェクトを直接返すことです。ポインタやnewの呼び出しはありません。コピーコンストラクターは、呼び出し元がヒープまたはスタックに格納できる独自の動物オブジェクトを取得するか、必要に応じてコンテナーにコピーできるようにします。
そう:
class AnimalLister
{
Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation
};
Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object
RVOは、ポインターの代わりにオブジェクトを返す方が実際には高速であることを意味します(コンパイラーは新しいオブジェクトを作成して呼び出し元のオブジェクトにコピーせず、呼び出し元のオブジェクトを直接使用するため)。
Scott Meyersによる徹底的な議論 で、彼は、shared_ptrまたはauto_ptrを使用するのが最善であると結論付けています。
または、COM風のアプローチに従い、単純な参照カウントを適用することもできます。
参照カウントが0に達すると、オブジェクトはそれ自体を削除します。
最終的にはshared_ptrが内部で行うことですが、何が起こっているかをより細かく制御でき、私の経験ではデバッグが容易です。 (これも非常にクロスプラットフォームです)。
私はまだshared_ptrに私の開発のチャンスをあまり与えていないので、それはあなたの目的に完全に役立つかもしれません。
オブジェクトが占有しているメモリを解放するときは、その特定のオブジェクトが不要になったときです。特定のケースでは、クラスAnimalListerのユーザーが、クラスAnimalの新しく割り当てられたオブジェクトへのポインターを要求しました。したがって、彼は、そのポインタ/オブジェクトが必要なくなったときにメモリを解放する責任があります。
AnimalLister lister;
Animal* a = lister.getNewAnimal();
a->sayMeow();
delete a;
私の意見では、この場合、何もオーバーエンジニアリングする必要はありません。 AnimalListerは、新しいAnimalオブジェクトを作成する単なるファクトリであり、それだけです。
Joshの答えは本当に好きですが、まだリストされていないので、別のパターンを投入するかもしれないと思いました。アイデアは、クライアントコードに動物の追跡を処理するように強制することです。
class Animal
{
...
private:
//only let the lister create or delete animals.
Animal() { ... }
~Animal() { ... }
friend class AnimalLister;
...
}
class AnimalLister
{
static s_count = 0;
public:
~AnimalLister() { ASSERT(s_count == 0); } //warn if all animals didn't get cleaned up
Animal* NewAnimal()
{
++count;
return new Animal();
}
void FreeAnimal(Animal* a)
{
delete a;
--s_count;
}
}