C++ で malloc と new アロケータを使い分ける

胡金庫 2023年10月12日
  1. C++ でダイナミックメモリを割り当てるために new 演算子を使用する
  2. new 演算子と std::unique_ptr を用いて C++ で動的メモリを確保する
  3. 関数 mallocrealloc/reallocarray を使って動的メモリを割り当てる
C++ で malloc と new アロケータを使い分ける

この記事では、C++ で mallocnew アロケータを使用するいくつかの方法を説明します。

C++ でダイナミックメモリを割り当てるために new 演算子を使用する

new は、C++ でダイナミックメモリを直接管理するための好ましいインターフェイスです。これは指定された型のオブジェクトを作成し、そのポインタを返します。new 演算子を使って割り当てられたオブジェクトはデフォルトで初期化されているため、組み込み型や複合型のオブジェクトにはガベージ値があり、使用する前に初期化する必要があります。

しかし、以下の例では、サイズ 10 の配列 int を確保しています。したがって、変数 arr1 に格納されたポインタの戻り値は 40 バイトのメモリを指していることになります。関数 initPrintIntVector は、実用的なコーディング例をよりよく示すために実装されているに過ぎません。いわゆるネイキッドポインタを使っているので、プログラムが終了する前に delete 演算子を使って確保したメモリを解放することが重要です。ただし、delete の後に括弧をつけることは、配列の各位置を解放するためにも必要です。

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  int *arr1 = new int[SIZE];

  initPrintIntVector(arr1, SIZE);

  delete[] arr1;
  return EXIT_SUCCESS;
}

出力:

 8; 380; 519; 536; 408; 666; 382; 244; 448; 165;

new 演算子と std::unique_ptr を用いて C++ で動的メモリを確保する

new 演算子は動的なメモリ割り当てのための優れたツールのように見えますが、集中的なメモリ操作を伴う大規模なコードベースでは、非常にエラーを起こしやすくなります。つまり、適切なタイミングでメモリリソースを解放することは非常に難しい問題であり、ほとんどの場合、メモリリークや予期せぬランタイムエラーが発生します。そのため、C++ 11 版以降の標準ライブラリでは、ポインタが指すメモリを自動的に削除するスマートポインタが追加されています。std::unique_ptr はスマートポインタの一種であり、与えられたオブジェクトを指すことを自分自身だけに許可します。割り当ては new 演算子を使って行われ、ポインタが使われた後は delete を呼び出さずにプログラムを終了できることに注意してください。

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  std::unique_ptr<int[]> arr2(new int[SIZE]);

  initPrintIntVector(arr2.get(), SIZE);

  return EXIT_SUCCESS;
}

出力:

985; 885; 622; 899; 616; 882; 117; 121; 354; 918;

関数 mallocrealloc/reallocarray を使って動的メモリを割り当てる

一方で、C++ のコードはオリジナルの C スタイルのアロケータ関数 malloc を呼び出すことができますが、これは現代の C++ 標準の動的なメモリ操作の方法としてはかなり古風な方法です。これはヒープ上のオブジェクトを割り当てるのに推奨される方法ではありませんが、プラス面では malloc の方がより柔軟な機能を提供します。

mallocsizeof オブジェクトを指定する単一の引数で呼び出され、C++ の対応する型にキャストすべき void* を返します。malloc で確保したメモリの利点は、reallocreallocarray 関数でメモリを拡張/縮小できることです。realloc 関数はオブジェクトへのポインタと新しいサイズを引数に取り、reallocarray 関数はポインタと要素数と各要素のサイズを引数に取ります。オブジェクトのメモリが拡張された場合、古い保存値はそのまま残り、新たに追加された要素は初期化されないことに注意してください。したがって、以下の例では、拡張された arr3 の要素を出力しているが、これはデモのためだけのものであり、実際のプログラムではそうではないはずです。

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

void printIntVector(int *arr, const int &size) {
  for (int i = 0; i < size; ++i) {
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  int *arr3 = static_cast<int *>(malloc(SIZE * sizeof(int)));
  //    int *arr3 = static_cast<int *>(malloc(SIZE * sizeof *arr3));
  //    int *arr3 = static_cast<int *>(malloc(sizeof(int[SIZE])));

  initPrintIntVector(arr3, SIZE);

  arr3 = static_cast<int *>(reallocarray(arr3, NEW_SIZE, sizeof(int)));
  // arr3 = static_cast<int *>(realloc(arr3, NEW_SIZE * sizeof(int)));
  printIntVector(arr3, NEW_SIZE);

  free(arr3);
  return EXIT_SUCCESS;
}

出力:

128; 346; 823; 134; 523; 487; 370; 584; 730; 268;
128; 346; 823; 134; 523; 487; 370; 584; 730; 268;  0;  0;  0;  0;  0;  0;  0;  0;  0;  0;
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C++ Memory