web-dev-qa-db-ja.com

クラスメンバーと明示的なスタック/ヒープの割り当て

次の4つのクラスがあるとします。

class A
{
    public:           
        A(void) : m_B()
        {
        }
    private:
        B m_B;
}

class B
{
    public:            
        B(void)
        {
           m_i = 1;
        }
    private:
        int m_i;
}

class C
{
    public:           
        C(void) 
        {
            m_D = new D();
        }
        ~C(void) 
        {
            delete m_D;
        }
    private:
        D *m_D;
}

class D
{
    public:           
        D(void)
        {
           m_i = 1;
        }
    private:
        int m_i;
}

4つのケースがあるとしましょう:

ケース1:Aはスタックに外部的に割り当てられ、Bはスタックに内部的に割り当てられます。

A myA1;

ケース2:Aはヒープに外部割り当てされ、Bはスタックに内部割り当てされます

A *myA2 = new A();

ケース3:Cはスタックに外部的に割り当てられ、Dはヒープに内部的に割り当てられます

C myC1;

ケース4:Cがヒープに外部的に割り当てられ、Dが内部的にヒープに割り当てられます

C *myC2 = new C();

これらのケースのそれぞれで何が起こっていますか?たとえば、ケース2では、ポインタmyA2がスタックに割り当てられ、Aオブジェクトがヒープに存在することを理解していますが、m_B属性についてはどうでしょうか。オブジェクトがヒープスペースに存在しても意味がなく、その属性がスコープから外れるため、ヒープ上のスペースも割り当てられていると想定しています。これが当てはまる場合、それは外部ヒープ割り当てが内部スタック割り当てをオーバーライドすることを意味しますか?

ケース3では、myC1がスタックに割り当てられますが、m_Dはヒープに割り当てられます。そこで何が起こるの? 2つの部分はメモリ間で分割されていますか?デストラクタから「deletem_D」を削除し、myC1がスコープ外になった場合、m_Dのヒープに割り当てられたスペースのメモリリークが発生しますか?

これを詳細に説明しているチュートリアル/記事があれば、リンクが欲しいです。

28
kbirk

「スタック/ヒープ割り当て」と「自動変数」を混同していると思います。

自動変数は、コンテキストから抜けると自動的に破棄されます。

スタック割り当ては、メモリが実行スタックに割り当てられるという事実です。そして、スタックに割り当てられた変数は自動変数です。

また、メンバーは自動変数ですは、その所有者が破棄されたときにデストラクタが呼び出されます。ポインタの場合、ポインタは破棄されますが、基になるオブジェクトは破棄されません。明示的にdeleteを呼び出す必要があります。基になるオブジェクトが確実に破棄されるようにするには、スマートポインターまたは一意のポインターを使用する必要があります。

別の言い方をすれば、deleteを呼び出さなければならない変数/メンバーは自動変数ではありません。

最後に、クラスのメンバーは、その所有者と同じメモリセグメントに割り当てられます。

あなたのコードでは:

  • A.m_Bは自動変数です。 Aがスタック上にある場合、Bも同様であり、Aがヒープ上にある場合、Bも同様です。
  • B.m_iおよびD.m_iは自動変数であり、所有者の同じメモリセグメントに割り当てられます
  • pointerC.m_Dは自動変数ですが、タイプDのポイントされたオブジェクトは自動変数ではありません。基になるオブジェクトを削除するには、ポインターでdeleteを明示的に呼び出す必要があります。したがって、ポインタC.m_Dは同じメモリセグメントに割り当てられますが、基になるオブジェクトには割り当てられません。それは明らかにnewによって割り当てられ、ヒープ上にあります。

そう:

  • ケース1:すべてがスタック上にあり、自動的に実行されます(つまり、自動的に破棄されます)。
  • ケース2:myA2はヒープ上にあり、自動ではありません(delete myA2にする必要があります)。そのメンバーm_B2は、myA2が破棄されると破棄される自動変数です。また、myA2はヒープ上にあるため、クラスの他のメンバーと同様に、m_Bもヒープ内の同じメモリ空間にあります。
  • ケース3:myC1はスタック上にあり、自動変数です。m_Dへのポインターもスタック上にありますが、m_Dが指すオブジェクトではありません。これは、ヒープ上のnewによって割り当てられます。
  • ケース4:ケース3と同じですが、myC2はヒープ上にあり、自動ではありません。したがって、myC2を削除する必要があります(これにより、m_Dが削除されます)。
39
Mesop

ケース1:「スタック」(自動ストレージ)上のすべて。スコープを終了すると、リソースが解放されます。

ケース2:myA2は「ヒープ」上にあるため、m_Bもそうであり、myA2によって使用されているリソースを解放することのみを心配する必要があります。 m_Bの場合、myA2は自動的に破棄されます。

ケース3:myC1はスタック上にあり、m_Dはヒープ上のDを指しますが、Cデストラクタが削除するため、myC1はスコープ外になり、動的に割り当てられたすべてのリソースがクリアされます。

ケース4:動的に割り当てられたmyC2、それに割り当てられたリソースを解放するために削除する必要があります。それを削除すると、コンストラクターが呼び出され、ケース3のように、コンストラクターがm_Dを処理します。

記事についてはよくわかりませんが、周りにはたくさんあると思います。しかし、私はいくつかを読むことをお勧めします 良いC++の本

6
juanchopanza

あなたのオブジェクトは組織化された記憶の一部です。オブジェクトはそのメンバーをスタックに割り当てません。それは単にそのメンバーで構成されます。

ケース2:オブジェクト全体がヒープ内に存在します。これは、オブジェクトのすべてのメンバーがヒープ内にあることを意味します。

ケース3:全体オブジェクトがスタックに存在します。秘訣は、myC1のメンバーであるのはDクラスインスタンスではなく、pointer-to-B物理的にmyC1のメンバーであるということです。したがって、myC1のメンバーはスタック上にあり、ヒープ内にあるDインスタンスを指します。

2
tonytony