mallocで確保したいポインタにstd::unique_ptrを適用する(カスタムDeleter関数)
C++にはmalloc/free
とnew/delete
という2つの動的メモリ確保/解放のパターンがあります。
malloc/free
は、元々 C 言語の関数で、そのまま C++ に入っています。以下のように使います。
int* numbers = static_cast<int*>(malloc(100 * sizeof(int)));
free(ptr);
new/delete
は、C++ で新しく追加された関数です。配列をアロケートする場合は、new/delete
かnew[]/delete[]
になります。以下のように使います。
int number = new int;
delete ptr;
int* numbers = new int[100];
delete[] numbers;
malloc
かnew/new[]
で確保したメモリはfree
かdelete/delete[]
で解放しないと、メモリーリークになってしまうため、自動的にデストラクタで解放してくれるstd::unique_ptr
のようなスマートポインタを使うことがお勧めされています。
new/delete
とnew[]/delete[]
の場合は、以下のようにstd::unique_ptr
をそのまま使えます。
#include <memory>
std::unique_ptr<int> number(new int); // delete で解放してくれる
std::unique_ptr<int[]> numbers(new int[100]); // delete[] で解放してくれる
でもmalloc/free
の場合はそう簡単にいきません。なぜかというと、std::unique_ptr
のデストラクタでメモリ解放してくれるデフォルトの Deleter 関数std::default_delete
はdelete
かdelete[]
を使うようになっているからです。malloc
で確保したメモリをdelete
かdelete[]
で解放すると未定義動作になって、何が起こるか分かりません。
{
std::unique_ptr<int[]> number(static_cast<int*>(malloc(100 * sizeof(int))));
} // 解放時に delete[] が使われるため未定義動作となる
幸いstd::unique_ptr
には、カスタムな Deleter 関数を指定できるコンストラクタも存在します。
以下のようにカスタムな Deleter を構造体のメンバー関数として定義してから、std::unique_ptr
のコンストラクタにテンプレート引数として指定します。
struct CustomDeleter
{
void operator()(int* p) { free(p); }
};
std::unique_ptr<int[], CustomDeleter> numbers(static_cast<int*>(malloc(100 * sizeof(int))));
でも毎回こういう構造体は定義したくないと思います。その代わりに、以下のようにstd::unique_ptr
のコンストラクタにカスタムな Deleter 関数をラムダ式関数として指定できます。
クラスの定義はこうなっていますね。
template <class T, class Deleter> class unique_ptr<T[], Deleter>; // unique_ptr の定義
unique_ptr(pointer p, Deleter& d) noexcept; // コンストラクタの定義
実際に書いてみると、こうなると思います。
#include <functional>
#include <memory>
std::unique_ptr<int[], std::function<void(int*)>> numbers
( static_cast<int*>(malloc(100 * sizeof(int)))
, [] (int* p) { free(p); }
);
このやり方はmalloc
に限定するというよりは、delete/delete[]
以外で解放しないといけないメモリに対すしてstd::unique_ptr
を使うテクニック、となると思います。是非参考にしてください。
Discussion