114

私は C++ を理解しようとしている C プログラマーです。多くのチュートリアルでは、次のようなスニペットを使用してオブジェクトのインスタンス化を示しています。

Dog* sparky = new Dog();

これは、後で行うことを意味します。

delete sparky;

これは理にかなっています。さて、動的メモリ割り当てが不要な場合、代わりに上記を使用する理由はありますか

Dog sparky;

スパーキーが範囲外になったら、デストラクタを呼び出させますか?

ありがとう!

4

9 に答える 9

168

それどころか、経験則として、ユーザー コードに新規作成/削除を含めるべきではないという範囲で、常にスタック割り当てを優先する必要があります。

あなたが言うように、変数がスタック上で宣言されると、その変数がスコープ外になると、そのデストラクタが自動的に呼び出されます。これは、リソースの有効期間を追跡し、リークを回避するための主要なツールです。

したがって、一般に、(new を呼び出すことによって) メモリ、ファイル ハンドル、ソケットなどのリソースを割り当てる必要があるたびに、コンストラクターがリソースを取得し、デストラクタがそれを解放するクラスにラップします。次に、スタック上にそのタイプのオブジェクトを作成すると、リソースがスコープ外になったときにリソースが解放されることが保証されます。そうすれば、メモリリークを確実に回避するために、新しい/削除のペアをどこでも追跡する必要がなくなります。

このイディオムの最も一般的な名前はRAIIです

また、専用の RAII オブジェクトの外側で何かを new で割り当てる必要があるまれなケースで、結果のポインターをラップするために使用されるスマート ポインター クラスも調べてください。代わりに、ポインターをスマート ポインターに渡します。スマート ポインターは、たとえば参照カウントによってその有効期間を追跡し、最後の参照がスコープ外になったときにデストラクタを呼び出します。標準ライブラリにはstd::unique_ptr、単純なスコープベースの管理があり、std::shared_ptr共有所有権を実装するために参照カウントを行います。

多くのチュートリアルでは、次のようなスニペットを使用してオブジェクトのインスタンス化を示しています...

あなたが発見したのは、ほとんどのチュートリアルがつまらないということです。;) ほとんどのチュートリアルでは、必要のないときに new/delete を呼び出して変数を作成したり、割り当ての有効期間を追跡するのに苦労したりするなど、お粗末な C++ のプラクティスを教えています。

于 2008-12-02T10:15:34.100 に答える
20

スタック上にあることは、割り当てと自動解放の点で有利かもしれませんが、いくつかの不利な点があります。

  1. スタックに巨大なオブジェクトを割り当てたくない場合があります。

  2. ダイナミック出動!次のコードを検討してください。

#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」が出力されます。Stack を使用するとどうなるか見てみましょう。

int main(void) {
  A a = B();
  a.f();
  return 0;
}

これは「A」を出力しますが、Java やその他のオブジェクト指向言語に精通している人には直感的ではないかもしれません。その理由は、インスタンスへのポインタがもうないからBです。代わりに、 のインスタンスが作成され、 type の変数にBコピーされます。aA

特に C++ を初めて使用する場合は、直感に反して発生することがあります。C では、ポインターがあり、それだけです。あなたはそれらの使い方を知っていて、いつも同じことをします。C++ では、そうではありません。この例で a をメソッドの引数として使用するとどうなるか想像してみてください - 物事はより複雑になり、aisAまたは or A*or even A&(call-by-reference) の場合に大きな違いが生じます。多くの組み合わせが可能であり、それらはすべて異なる動作をします。

于 2013-06-29T11:05:18.297 に答える
13

このアンチパターンは、&address-of演算子を十分に理解していない人から見たものです。ポインターを使用して関数を呼び出す必要がある場合は、常にヒープに割り当てて、ポインターを取得します。

void FeedTheDog(Dog* hungryDog);

Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;

Dog goodDog;
FeedTheDog(&goodDog);
于 2008-12-02T17:26:27.460 に答える
13

ポインタを使用する理由は、malloc で割り当てられた C でポインタを使用する理由とまったく同じです。オブジェクトを変数よりも長く存続させたい場合です。

new 演算子を回避できる場合は、使用しないことを強くお勧めします。特に例外を使用する場合。一般に、コンパイラにオブジェクトを解放させる方がはるかに安全です。

于 2008-12-02T09:25:53.810 に答える
7

ヒープを非常に重要な不動産として扱い、慎重に使用してください。基本的な経験則は、可能な限りスタックを使用し、他に方法がない場合はヒープを使用することです。オブジェクトをスタックに割り当てることで、次のような多くの利点が得られます。

(1)。例外が発生した場合のスタックの巻き戻しについて心配する必要はありません

(2)。ヒープ マネージャーによって必要以上のスペースが割り当てられることによるメモリの断片化について心配する必要はありません。

于 2008-12-02T17:06:25.893 に答える
5

私が心配する唯一の理由は、Dog がヒープではなくスタックに割り当てられるようになったことです。したがって、Dog のサイズがメガバイトの場合、問題が発生する可能性があります。

新規/削除ルートに進む必要がある場合は、例外に注意してください。このため、auto_ptr またはブースト スマート ポインター型のいずれかを使用して、オブジェクトの有効期間を管理する必要があります。

于 2008-12-02T09:28:52.883 に答える
1

スタックに割り当てることができる場合、(ヒープ上で) new する理由はありません (何らかの理由で小さなスタックがあり、ヒープを使用したい場合を除きます)。

ヒープに割り当てたい場合は、標準ライブラリの shared_ptr (またはそのバリアントの 1 つ) の使用を検討することをお勧めします。shared_ptr へのすべての参照が存在しなくなったら、削除を処理します。

于 2008-12-02T09:30:51.383 に答える
0

オブジェクトを動的に作成することを選択する理由として、他の誰も言及していない追加の理由があります。動的なヒープ ベースのオブジェクトを使用すると、ポリモーフィズムを利用できます。

于 2013-03-13T11:50:34.220 に答える
-4

Visual Studio でも同じ問題が発生しました。以下を使用する必要があります。

yourClass->classMethod();

それよりも:

yourClass.classMethod();

于 2012-08-12T23:31:47.957 に答える