3

C++のメモリ割り当てについての理解をテストしようとしています。

次のプログラムの場合:

{
    int a=0;
}

はスタックから割り当てられているのでa、変数がスコープ外になったときに解放する必要がありますよね?

さて、簡単です。この場合はどうですか?

{
    Matrix m(50, 20);
}

マトリックスクラスがあり、50行20列の新しいクラスを作成しているとしましょう。明らかに、実行時に50と20が投入される可能性があるため、すべてのメモリをスタックから割り当てることができるわけではありません。ですから、コンストラクターのどこかで、ヒープからメモリを割り当てていると思います。

それがスコープ外になると、デストラクタmが呼び出されますか?そして、その記述子は、割り当てたメモリの割り当てを解除(削除)する必要がありますか?

今、それは本当に難しくなります:

{
    Matrix t;
    {
        Matrix m(50, 20);
        t=m;
    }
}

それではどうなりますか?mのメモリ位置に割り当てられませんか?または、mのデータのコピーを実行しますか?tがmへの参照である場合、mがスコープ外になるとどうなりますか?mのデストラクタは呼び出されますか?または、tがスコープ外になるまでt / mのデストラクタを呼び出すのを待ちますか?

4

5 に答える 5

6

それが範囲外になると、 m のデストラクタが呼び出されますか? そして、そのデストラクターは、割り当てたメモリの割り当てを解除 (削除) する必要がありますか?

はい、一般的にはい。

今、それは本当に難しくなります:

{
    Matrix t;
    {
        Matrix m(50, 20);
        t=m;
     }
}

その後どうなりますか?t は m のメモリ位置に割り当てられますか? それともmでデータのコピーをしますか?

何が起こるかというと、代入演算子が呼び出されます:

t.operator=(m);

Matrix有効なセマンティクスを保証するのは、 の実装者であるあなた次第です。考えられるアプローチはいくつかあります。

  1. 代入演算子はmのデータのコピーを作成できます。この場合、寿命と所有権に問題はありません。ただし、このアプローチでは、割り当てにコストがかかります。
  2. 代入演算子はt、 と同じデータを指し示すことができmます。これは実行可能かもしれませんが、データの有効期間が正しく管理されていること、および一方のマトリックスを変更しても他方が予期せず変更されないようにするために、多くの注意が必要です。これを行う 1 つの方法は、データへの参照カウント ポインターを保持し、データを変更するときにコピー オン ライトを使用することです。の一部の古い実装はstd::stringこのタイプです。
于 2013-01-21T17:53:34.390 に答える
1

実際、それはかなり簡単です。

最初のケースでは、あなたは正しいです。自動割り当て、自動割り当て解除。

2 番目のケースでは、実際には違いはありません。Matrix クラス コンストラクターは必要な追加メモリを処理し、そのデストラクタはそのメモリの割り当てを解除する必要があります。

3 番目のクラスでは、内側のスコープ変数が外側のスコープ変数にコピーされます。Matrix クラスは 3 つのルールに従う必要があるため、コピーは正しく処理する必要があります。

これはすべて、Matrix の適切な実装を前提としています。

于 2013-01-21T17:50:56.253 に答える
0

C++ のメモリ割り当ては非常に単純で、オブジェクト スコープも同様です。C++ クラスの設計はそれほど単純ではありません。

あなたの例では、ブレースを閉じるたびに、ローカル オブジェクト a または m または t (最後の例のブレースの外側のセット) がスコープ外になり、それらのデストラクタが呼び出されます。int の場合、デストラクタは単純で、スタック上のオブジェクトを削除します。m の場合、これはクラスMatrixのカスタム デストラクタであり、立派なライブラリの場合は、オブジェクトの割り当てを正しく解除すると想定できます。

t を複雑にしているのはデストラクタではなく、m からの代入です。行列クラスのほとんどの実装では、t = m の結果、m の内容が t にコピーされ、ヒープ上に独自のメモリが作成されます。次に、各オブジェクトがスコープ外になると、対応するメモリの割り当てが解除されます。

実装がより複雑な場合は、各オブジェクトの破棄を正しく処理することを確認するのはクラス デザイナーの責任です (また、それができるようにライブラリを正しく使用する必要があります)。

于 2013-01-21T18:02:22.563 に答える
0
What happens then? Does t get assigned to the memory location of m?

デフォルトでメンバーごとのコピーを行うコピーコンストラクターの呼び出しです。

ただし、代入演算子をオーバーロードした場合は、参照を代入するか、完全に新しいオブジェクトを作成することができます。

質問の残りの部分:

デストラクタへの呼び出しは、明示的に呼び出すか、デフォルトのままにするかによって異なります。デフォルトでは、変数またはオブジェクトがスコープ外にあるときに呼び出されます。

于 2013-01-21T18:09:51.643 に答える
0

あなたはintで正しいです。割り当てが呼び出されると、スタック上で int サイズのスペースが占有され、変数がスコープ外になると、その int がスタックから「ポップオフ」され、メモリが解放されて再び使用できるようになります。

マトリックスでは、あなたは 100% 正しいわけではありません。実行時にデータが割り当てられるからといって、スタックに配置する余地がある限り、データをスタックに割り当てることができないわけではありません。ここで何が起こるかというと、マトリックスを作成すると、それがスタックにプッシュされます。スタック上で占有するスペースの量は、管理方法によって異なります。コンストラクター内で、ヒープにメモリを割り当てるか、単にスタック スペースの大きなチャンクを占有する場合があります。スタックに余裕がない場合、実行時にスタック オーバーフロー例外が発生します。

ただし、構築時にメモリを割り当てる場合は、破棄時にクリーンアップする必要があります。しかし、そうする必要はなく、場合によってはそうすべきではありません (たとえば、別のオブジェクトと共有/共有しているために、そのメモリを実際に「所有」していない場合)。

あなたの最後のケースでは、何が起こるかは次のとおりです。

Matrix t;

これにより、クラスの既定のコンストラクターを使用して Matrix が作成されます。ここでメモリを予約するかどうかは、そのコンストラクタの内容に依存します。メモリを予約する場合もありますが、予約しない場合もあります。

Matrix m(50, 20);

これにより、クラスの別のコンストラクターを使用して Matrix が作成されます。繰り返しますが、メモリを予約するかもしれませんが、予約しないかもしれません (たとえば、「実際の」データが追加されるまで、スタックにあるもの以外のスペースを占有しないかもしれません)。

t=m;

ここで何が起こるかは、クラス次第です。この時点で、これはコンストラクターではなく、t の代入関数です。

Matrix& operator=(const Matrix& m){
    ....
}

呼ばれています。一部の割り当てコンストラクターはデータをコピーし、一部はデータへのポインターをコピーしますが、実際のデータ自体はコピーしません。ただし、後者の場合、クラスは、データの削除を拒否し、代わりに t のコンストラクターに依存して、m がスコープ外になる条件を処理する必要があります。しかし、繰り返しになりますが、そうする必要はなく、悪いことをする可能性があります (特に、これを制御するのが複雑になるため)。

ここから重要なことは、クラスの作成と破棄のレベルに到達すると、メモリの処理方法はクラスの実装に依存するということです。

于 2013-01-21T18:17:48.360 に答える