c++ vectorのpush_backで確保される領域について

vectorのpush_backでヒープ領域に確保される

C++STLにあるvectoのpush_back()emplace_back()で確保される領域はヒープ領域です。

#include "stdafx.h"
#include <vector>

class MyClass
{
private:
    int m_Array[1000];//適当に大きめの領域を確保
};

int main()
{
    
    std::vector<MyClass> List;

    for (int i = 0; i < 500; i++)
    {
        MyClass mc;
        List.emplace_back(mc);
    }

//ここでブレークポイントをかける
        
    return 0;
}

上記のソースコードブレークポイントをかけると以下のようにヒープ領域に確保されます。

f:id:Brave345:20200305142833p:plain

これはスナップショットで検知した現在ヒープ領域に確保されてるもの表示しています。

これを見ると2844039バイトヒープ領域に確保されています。

これらの領域を開放するにはvectorのデストラクタを呼ぶ必要があります。

#include "stdafx.h"
#include <vector>

class MyClass
{
private:
    int m_Array[1000];//適当に大きめの領域を確保
};

int main()
{
    {
     std::vector<MyClass> List;

      for (int i = 0; i < 500; i++)
      {
          MyClass mc;
         List.emplace_back(mc);
      }
    }
//ここでブレークポイントをかける
//ここの時点でヒープ領域に確保しものは解放される
    return 0;
}

注意点

cleareraseでは確保したヒープ領域は解放されません

#include "stdafx.h"
#include <vector>

class MyClass
{
private:
    int m_Array[1000];
};

int main()
{
    std::vector<MyClass> List;

    for (int i = 0; i < 500; i++)
    {
        MyClass mc;
        List.emplace_back(mc);
    }

    List.erase(List.begin(),  List.end());
        //List.clear();

//ここでブレークポイントをかけてもヒープ領域は解放されてない

    return 0;
}

問題

普通に使用してる分には、スコープがきれた際に解放されるので大丈夫ですが、staticなvectorを用意すると話は変わります。 staticにするとアプリケーションを終了するまでメモリが解放されません。

#include "stdafx.h"
#include <vector>

class MyClass
{
private:
    int m_Array[1000];
};

static std::vector<MyClass> List;

void GameLoop()
{
    while (true)
    {
        MyClass mc;
        List.push_back(mc);
        List.clear();//ヒープ領域は解放されてない!
    }
}


int main()
{
    GameLoop();
    return 0;
}

上記のコードだと常にヒープに4000バイト(int 1000個分)確保された状態になってしまいます。

なぜ確保する領域が増え続けないいうと確保した領域を使いまわしているため、常に4000バイトだけ確保されています。

ですがvectorの要素数が増えるとその分ヒープから確保しなければならないので、

素数が増える程、常にヒープに確保している領域が大きくなってしまいます

解決法

swapを使用することで強制的にデストラクタを使用させることで使用してないヒープ領域を開放することができます。

#include "stdafx.h"
#include <vector>

class MyClass
{
private:
    int m_Array[1000];
};

int main()
{
    
        std::vector<MyClass> List;

        for (int i = 0; i < 500; i++)
        {
            MyClass mc;
            List.emplace_back(mc);
        }

        List.erase(List.begin(), List.begin() + 50);
        //List.clear();
        std::vector<MyClass>(List).swap(List);//使用してないヒープ領域を開放
    
    return 0;
}

終わりに

c++ではnew以外にもヒープ領域を確保されることがあることを覚えておきましょう。

でないとメモリリークが判明した時にずっとありもしないnewの開放ミスだけを探し続けてしまうハメなるかもしれません。