回答:
逆に、経験則として、ユーザーコードにnew / deleteが含まれないように、常にスタック割り当てを優先する必要があります。
あなたが言うように、変数がスタックで宣言されている場合、そのデストラクタはスコープ外になると自動的に呼び出されます。これは、リソースの寿命を追跡し、リークを回避するための主要なツールです。
したがって、一般に、リソースを割り当てる必要があるたびに、それが(newを呼び出すことによる)メモリ、ファイルハンドル、ソケットなどに関係なく、コンストラクタがリソースを取得し、デストラクタがリソースを解放するクラスにそれをラップします。次に、そのタイプのオブジェクトをスタック上に作成できます。これにより、リソースがスコープ外になったときに解放されることが保証されます。そうすることで、メモリリークを確実に回避するために、新しい/削除のペアをどこでも追跡する必要がなくなります。
このイディオムの最も一般的な名前はRAIIです
また、専用のRAIIオブジェクトの外側に新しいものを割り当てる必要があるというまれなケースで、結果のポインターをラップするために使用されるスマートポインタークラスを調べます。代わりに、ポインタをスマートポインタに渡します。このポインタは、たとえば参照カウントなどによってその有効期間を追跡し、最後の参照がスコープから外れたときにデストラクタを呼び出します。標準ライブラリはstd::unique_ptr
、単純なスコープベースの管理のためにあり、std::shared_ptr
共有所有権を実装するために参照カウントを行います。
多くのチュートリアルは、次のようなスニペットを使用したオブジェクトのインスタンス化を示しています...
だから、あなたが発見したのは、ほとんどのチュートリアルがひどいことです。;)ほとんどのチュートリアルは、必要のないときに変数を作成するためにnew / deleteを呼び出したり、割り当ての存続期間を追跡するのに苦労したりするなど、お粗末なC ++プラクティスを教えています。
スタック上に物を置くことは、割り当てと自動解放の点で有利かもしれませんが、いくつかの欠点があります。
スタックに巨大なオブジェクトを割り当てたくない場合があります。
ダイナミック派遣!このコードを考えてみましょう:
#include <iostream>
class A {
public:
virtual void f();
virtual ~A() {}
};
class B : public A {
public:
virtual void f();
};
void A::f() {cout << "A";}
void B::f() {cout << "B";}
int main(void) {
A *a = new B();
a->f();
delete a;
return 0;
}
これは「B」を印刷します。スタックを使用すると何が起こるか見てみましょう:
int main(void) {
A a = B();
a.f();
return 0;
}
これは「A」を出力しますが、Javaや他のオブジェクト指向言語に精通している人には直感的ではないかもしれません。その理由は、B
もうインスタンスのインスタンスへのポインタがないためです。代わりに、のインスタンスB
が作成されa
、タイプの変数にコピーされますA
。
特にC ++を初めて使用する場合は、いくつかのことが直感的に理解できないことがあります。Cではポインタがあり、それだけです。あなたはそれらを使う方法を知っていて、彼らはいつも同じことをします。C ++ではこれは当てはまりません。この例でをメソッドの引数として使用するとどうなるか想像してみてください。物事はより複雑になりa
、タイプA
または(A*
またはA&
参照渡し)の場合でも、大きな違いが生じます。多くの組み合わせが可能であり、それらはすべて異なる動作をします。
&のアドレス演算子をまったく知らない人々からこのアンチパターンを見てきました。ポインターを使用して関数を呼び出す必要がある場合は、常にヒープに割り当ててポインターを取得します。
void FeedTheDog(Dog* hungryDog);
Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;
Dog goodDog;
FeedTheDog(&goodDog);
ヒープを非常に重要な不動産として扱い、非常に慎重に使用してください。基本的な経験則は、可能な限りスタックを使用し、他に方法がない場合は常にヒープを使用することです。オブジェクトをスタックに割り当てることにより、次のような多くの利点を得ることができます。
(1)。例外が発生した場合にスタックの巻き戻しを心配する必要はありません
(2)。ヒープマネージャが必要とするよりも多くの領域を割り当てることによって引き起こされるメモリの断片化について心配する必要はありません。
スタックに割り当てることができるとき(ヒープ上で)を新しくする理由はありません(何らかの理由で小さなスタックがあり、ヒープを使用したい場合を除きます)。
ヒープに割り当てたい場合は、標準ライブラリのshared_ptr(またはそのバリアントの1つ)の使用を検討してください。shared_ptrへのすべての参照が存在しなくなったら、これで削除が処理されます。
オブジェクトを動的に作成することを選択する理由は他にはありませんが、他にも理由があります。動的なヒープベースのオブジェクトを使用すると、ポリモーフィズムを利用できます。
Visual Studioでも同じ問題が発生しました。使用する必要があります:
yourClass-> classMethod();
のではなく:
yourClass.classMethod();