10

これが「あなたの好みは何ですか」という質問の 1 つとして解釈される可能性があることは理解していますが、次の方法のいずれかを選択する理由を知りたいです。

次のような超複雑なクラスがあるとします。


class CDoSomthing {

    public:
        CDoSomthing::CDoSomthing(char *sUserName, char *sPassword)
        {
            //Do somthing...
        }

        CDoSomthing::~CDoSomthing()
        {
            //Do somthing...
        }
};

グローバル関数内でローカル インスタンスを宣言するにはどうすればよいですか?


int main(void)
{
    CDoSomthing *pDoSomthing = new CDoSomthing("UserName", "Password");

    //Do somthing...

    delete pDoSomthing;
}

- また -


int main(void)
{
    CDoSomthing DoSomthing("UserName", "Password");

    //Do somthing...

    return 0;
}
4

7 に答える 7

27

オブジェクトの有効期間を現在のブロックを超えて延長する必要がない限り、ローカル変数を優先します。(ローカル変数は 2 番目のオプションです)。メモリ管理について心配するよりも簡単です。

PS別の関数に渡す必要があるため、ポインターが必要な場合は、アドレス演算子を使用してください。

SomeFunction(&DoSomthing);
于 2009-02-21T04:18:26.120 に答える
22

変数をスタックで宣言する場合とヒープで宣言する場合の主な考慮事項は 2 つあります。有効期間の制御とリソース管理です。

オブジェクトの有効期間を厳密に制御できる場合、スタックへの割り当ては非常にうまく機能します。つまり、そのオブジェクトのポインターまたは参照を、ローカル関数のスコープ外のコードに渡さないということです。これは、out パラメータ、COM 呼び出し、新しいスレッドがないことを意味します。非常に多くの制限がありますが、現在のスコープからの通常または例外的な終了時に、オブジェクトを適切にクリーンアップできます (ただし、仮想デストラクタを使用したスタックの巻き戻し規則を読みたい場合があります)。スタック割り当ての最大の欠点は、スタックが通常 4K または 8K に制限されているため、何をスタックに入れるかを慎重に検討することです。

一方、ヒープに割り当てるには、インスタンスを手動でクリーンアップする必要があります。これはまた、インスタンスの存続期間を制御する方法に多くの自由があることを意味します。これは、次の 2 つのシナリオで行う必要があります。a) そのオブジェクトをスコープ外に渡す場合。または b) オブジェクトが大きすぎて、スタックに割り当てるとスタック オーバーフローが発生する可能性があります。

ところで、これら 2 つの間の良い折衷案は、オブジェクトをヒープに割り当て、そのオブジェクトへのスマート ポインターをスタックに割り当てることです。これにより、スコープの終了時に自動クリーンアップを取得しながら、貴重なスタック メモリを無駄にしないことが保証されます。

于 2009-02-21T04:32:38.800 に答える
13

2 番目の形式は、いわゆる RAII (Resource Acquisition Is Initialization) パターンです。最初のものよりも多くの利点があります。

を使用する場合は、自分自身newを使用するdelete必要があり、例外がスローされた場合でも常に削除されることを保証します。そのすべてを自分で保証する必要があります。

2 番目の形式を使用すると、変数がスコープ外になると、常に自動的にクリーンアップされます。また、例外がスローされた場合、スタックはアンワインドされ、クリーンアップされます。

したがって、RAII (2 番目のオプション) を優先する必要があります。

于 2009-02-21T04:23:23.300 に答える
6

これまでに述べたことに加えて、特にメモリ割り当てを集中的に使用するアプリケーションでは、考慮すべきパフォーマンスに関する追加の考慮事項があります。

  1. を使用newすると、ヒープからメモリが割り当てられます。集中的な (非常に頻繁な) 割り当てと割り当て解除の場合、次の点で高い代償を払うことになります。
    • ロック: ヒープは、プロセス内のすべてのスレッドによって共有されるリソースです。ヒープに対する操作では、ヒープ マネージャーでのロックが必要になる場合があり (ランタイム ライブラリで自動的に行われます)、処理が大幅に遅くなる可能性があります。
    • 断片化: ヒープ フラグメント。malloc/new および free/delete が戻るのにかかる時間が 10 倍になることがあります。これは、断片化されたヒープを管理するのにより多くの時間がかかり、より多くのスレッドが修復ロックを待機するキューに入るために、上記のロックの問題と組み合わされます。(Windows では、ヒープ マネージャーに設定できる特別なフラグがあるため、断片化をヒューリスティックに削減しようとします。)
  2. RAII パターンを使用すると、メモリはスタックから単純に削除されます。スタックはスレッドごとのリソースであり、断片化せず、ロックも関係なく、メモリの局所性 (つまり、CPU レベルでのメモリ キャッシング) の点で有利になる場合があります。

そのため、短期間 (またはスコープ付き) のオブジェクトが必要な場合は、間違いなく 2 番目のアプローチ (ローカル変数、スタック上) を使用してください。スレッド間でデータを共有する必要がある場合は、new/malloc(一方では、一方で、これらのオブジェクトは通常、十分に長寿命であるため、ヒープ マネージャーに対して本質的に 0 コストを支払う必要があります。)

于 2009-02-21T04:43:21.910 に答える
3

2 番目のバージョンは、例外がスローされた場合にスタックをアンワインドします。最初はしません。それ以外はあまり違いがわかりません。

于 2009-02-21T04:20:34.710 に答える
2

2つの最大の違いは、newがオブジェクトへのポインタを開始することです。

newなしでオブジェクトを作成することにより、開始されたオブジェクトはスタックに格納されます。newで開始された場合は、ヒープ上に作成された新しいオブジェクトへのポインターを返します。実際には、新しいオブジェクトを指すメモリアドレスを返します。これが発生した場合、変数をメモリ管理する必要があります。変数の使用が終了したら、メモリリークを回避するために、変数に対してdeleteを呼び出す必要があります。new演算子がないと、変数がスコープ外になると、メモリが自動的に解放されます。

したがって、現在のスコープ外で変数を渡す必要がある場合は、newを使用する方が効率的です。ただし、一時変数、または一時的にのみ使用されるものを作成する必要がある場合は、メモリ管理について心配する必要がないため、オブジェクトをスタックに配置する方が適切です。

于 2009-02-21T05:36:50.520 に答える
0

Mark Ransom は正しいです。またnew、変数をパラメーターとして CreateThread 風の関数に渡す場合は、インスタンス化する必要があります。

于 2009-02-21T04:21:41.933 に答える