C++ メモリ管理ライブラリ<memory>

<memory>は C++ 標準ライブラリのヘッダファイルであり、動的メモリ管理のためのテンプレートと関数を含んでいます。

C++ では、メモリ管理は重要な概念です。動的メモリ管理により、プログラムは実行時にメモリを割り当てたり解放したりできます。これはサイズが不定のデータ構造を扱う場合に非常に役立ちます。しかし、不適切なメモリ管理は、メモリリークやダングリングポインタなどの問題を引き起こす可能性があります。

<memory>このヘッダファイルは、スマートポインタなどのツールを提供し、開発者が動的メモリをより安全に管理するのを助けます。

スマートポインタ

スマートポインタは<memory>ヘッダファイルの中核となる内容です。これらは C++11 で導入された機能で、動的に割り当てられたメモリを自動管理するために使用されます。スマートポインタの主な型は次のとおりです。

  • std::unique_ptr:排他的所有権を持つスマートポインタで、同時に1つだけがunique_ptr特定のメモリを指すことができます。
  • std::shared_ptr:共有所有権を持つスマートポインタで、複数のshared_ptrshared_ptr が同じメモリを指すことができ、メモリは最後のものがshared_ptr破棄されたときに解放されます。
  • std::weak_ptr:弱参照スマートポインタで、shared_ptrstd::shared_ptr と組み合わせて使用し、循環参照によるメモリリークを回避します。

例

使用std::unique_ptr

例

#include <iostream>
#include <memory>

class MyClass {
public:
    void doSomething() {
        std::cout << "Doing something" << std::endl;
    }
};

int main() {
    std::unique_ptr<MyClass> myPtr(new MyClass());
    myPtr->doSomething(); // スマートポインタを使用してメンバ関数を呼び出す

    // main 関数が終了すると、myPtr が破棄され、MyClass オブジェクトのメモリが自動的に解放されます
    return 0;
}

出力結果:

Doing something

使用std::shared_ptr

例

#include <iostream>
#include <memory>

class MyClass {
public:
    void doSomething() {
        std::cout << "Doing something" << std::endl;
    }
};

int main() {
    std::shared_ptr<MyClass> myPtr1(new MyClass());
    std::shared_ptr<MyClass> myPtr2 = myPtr1;

    myPtr1->doSomething(); // myPtr1 を使用してメンバ関数を呼び出す
    myPtr2->doSomething(); // myPtr2 を使用してメンバ関数を呼び出す

    // myPtr1 と myPtr2 の両方が破棄されると、MyClass オブジェクトのメモリが解放されます
    return 0;
}

出力結果:

Doing something
Doing something

使用std::weak_ptr

std::weak_ptr通常は単独では使用せず、std::shared_ptr とstd::shared_ptr組み合わせて使用し、循環参照の問題を解決します。

例

#include <iostream>
#include <memory>

class Node {
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;

    Node() : next(nullptr), prev() {}
};

int main() {
    std::shared_ptr<Node> node1 = std::make_shared<Node>();
    std::shared_ptr<Node> node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->prev = node1;

    // 循環参照ですが、weak_ptr を使うことでメモリリークを回避しています
    return 0;
}

この例では、node1とnode2循環参照が形成されています。weak_ptr の存在により、prevであるweak_ptrshared_ptr がnode1とnode2のshared_ptr破棄されると、それらが指していたメモリも正しく解放されます。

アロケータ

アロケータは <memory> のもう一つの重要な部分です。

アロケータはコンテナにメモリを割り当てるために使用されます。標準ライブラリのすべてのコンテナは、メモリ割り当てを処理するためにアロケータを使用します。

std::allocator

std::allocator は標準アロケータで、基本的なメモリ割り当てと解放機能を提供します。

例

#include <memory>
#include <vector>

int main() {
    std::allocator<int> alloc;
    int* p = alloc.allocate(1); // メモリを割り当てる
    alloc.construct(p, 42); // オブジェクトを構築する

    std::cout << *p << std::endl;

    alloc.destroy(p); // オブジェクトを破棄する
    alloc.deallocate(p, 1); // メモリを解放する

    return 0;
}

その他のメモリ管理ツール

std::align

std::align はポインタのアライメントを調整するために使用され、割り当てられたメモリが特定のアライメント要件を満たすことを保証します。

例

#include <memory>
#include <iostream>

int main() {
    alignas(16) char buffer[64];
    void* p = buffer;
    size_t space = sizeof(buffer);

    void* aligned_ptr = std::align(16, sizeof(int), p, space);
    if (aligned_ptr) {
        std::cout << "Memory aligned\n";
    } else {
        std::cout << "Memory alignment failed\n";
    }

    return 0;
}

<memory>このヘッダファイルは、C++ 標準ライブラリにおいて動的メモリ管理を扱う重要な部分です。

スマートポインタ(std::shared_ptr など)をunique_ptr、shared_ptrとweak_ptr使用することで、開発者はメモリをより安全かつ効率的に管理し、よくあるメモリ管理エラーを回避できます。この記事が、初心者が C++ の<memory>ヘッダファイルをよりよく理解し、使用する助けになることを願っています。

その他の拡張