std::unique_ptr (c++11)

std::unique_ptr です。auto_ptr が deprecated(廃止予定)になりましたので、今後は shared_ptr, unique_ptr がスマートポインタの主流になっていくことでしょう。
auto_ptr の後継という意味では unique_ptr の方がより近いかもしれません。

unique_ptr は下記2つの形式をサポートします。

  1. template<class T, class Deleter = std::default_delete<T>> class unique_ptr;
  2. template <class T, class Deleter> class unique_ptr<T[], Deleter>;
    ⇨ 配列 T[] をサポートします。

 

 

 

1. unique_ptr を使ってみる

まずは unique_ptr を使ってみます。shared_ptr とほとんど同じ感じで使用できますが、コピーはできません。moveを使って管理者の譲渡と明示的に行う必要があります。

 

1-1. その1

最初にシンプルな unique_ptr 使用例を紹介します。

 

unique_ptr を直接使用する例を記載します。

int 型の生成は、以下のように使用します。

std::unique_ptr up(new int);

 

値の初期化を一緒に行いたい場合、以下のように使用します。下記例は 10 で初期化しています。

std::unique_ptr up(new int(10));

 

配列を確保する場合は、以下のように使用します。

std::unique_ptr arr_up(new int[5]);

 

配列の値を初期化と一緒に行いたい場合、以下のように使用します。下記例は {1,2,3,4,5} で初期化しています。

std::unique_ptr arr_up(new int[5]{1,2,3,4,5});

 

make_unique による例を記載します。

int 型の生成は、以下のように使用します。

auto up = std::make_unique<int>();

 

値の初期化を一緒に行いたい場合、以下のように使用します。下記例は 77 で初期化しています。

auto up = std::make_unique<int>(77);

 

配列を確保する場合、以下のように使用します。下記例では、配列数 5 です。
make_unique を使って初期化を一緒に行うことはできないようです。

auto arr_up = std::make_unique<int[]>(5); // 要素はデフォルト初期化(ゼロでない場合あり)
for (int i = 0; i < 5; ++i) {
    arr_up[i] = (i + 1) * 10;
}

 

 

上記内容を含む c++ プログラムを以下で紹介します。

 

ソースコード:

["unique_ptr_01.cpp"]

#include <iostream>     // for std::cout
#include <new>          // for std::nothrow, placement new
#include <memory>       // for std::make_unique
 
/**
 * @brief std::unique_ptr, std::make_unique のシンプルなサンプル
 */
 
int main() {
    std::cout << "-- uqique_ptr simple examples start --\n";

    // 1) 単一 int の std::unique_ptr
    {
        std::unique_ptr<int> up(new int(10));
        std::cout << "*up = " << *up << "\n";
        // delete は自動的に行われる
    }

    // 2) int 配列の std::unique_ptr
    {
        std::unique_ptr<int[]> arr_up(new int[5]{1,2,3,4,5});
        for (int i = 0; i < 5; ++i) {
            std::cout << "arr_up[" << i << "] = " << arr_up[i] << "\n";
        }
        // delete[] は自動的に行われる
    }

    // 3) 単一 int 生成、std::make_unique 版
    {
        auto up = std::make_unique<int>(77);
        std::cout << "unique_ptr owns = " << *up << "\n";
        // delete は自動的に行われる
    }

    // 4) 配列 int 生成、std::make_unique 配列版(C++14 以降)
    {
        auto arr_up = std::make_unique<int[]>(5); // 要素はデフォルト初期化(ゼロでない場合あり)
        for (int i = 0; i < 5; ++i) {
            arr_up[i] = (i + 1) * 10;
        }
        std::cout << "make_unique<int[]> contents:";
        for (int i = 0; i < 5; ++i) {
            std::cout << ' ' << arr_up[i];
        }
        std::cout << '\n';
        // delete[] は自動的に行われる
    }

    std::cout << "-- uqique_ptr simple examples end --\n";
    return 0;
}

 

ビルド方法:

g++ -std=c++17 -Wall -Wextra -Wpedantic -O2 unique_ptr_01.cpp -o unique_ptr_01.out

 

実行結果:

$ ./unique_ptr_01.out 
-- uqique_ptr simple examples start --
*up = 10
arr_up[0] = 1
arr_up[1] = 2
arr_up[2] = 3
arr_up[3] = 4
arr_up[4] = 5
unique_ptr owns = 77
make_unique<int[]> contents: 10 20 30 40 50
-- uqique_ptr simple examples end --
$ 

 

 

1-2. その2

前の節で unique_ptr の簡単な使用方法について記載しました。

でも「本当に解放処理を自動的にやってくれてるのか心配」ですよね。

本節では、unique_ptr で class インスタンスを生成して、delete 無しで自動的にデストラクタが実行されることを確認してみます。

 

[環境]

コンパイラ : g++ (Ubuntu 13.3.0-6ubuntu2~24.04), 13.3.0
OS : Ubuntu 24.04 (WSL)  

 

[ソースコード: "unique_ptr.cpp"]

#include <iostream>     // cout, endl
#include <string>       // string
#include <memory>       // unique_ptr
#include <cstdlib>      // EXIT_SUCCESS

class Person {
private:
    std::string name_;
    std::string phone_;

public:
    /**
     * @brief デフォルトコンストラクタ
     *        空の名前と電話番号で委譲コンストラクタを呼ぶ
     */
    Person()
        : Person("", "")                // 委譲コンストラクタ
    {
    }

    /**
     * @brief コピーコンストラクタ
     * @param rhs コピー元
     */
    Person(const Person& rhs)
        : Person(rhs.name_, rhs.phone_)
    {
    }

    /**
     * @brief 値を指定するコンストラクタ
     * @param name 氏名
     * @param phone 電話番号
     */
    Person(const std::string& name, const std::string& phone)
        : name_(name), phone_(phone)
    {
        std::cout << "Person::Person()" << std::endl;
    }

    /**
     * @brief デストラクタ
     */
    virtual ~Person()
    {
        std::cout << "Person::~Person()" << std::endl;
    }

    /**
     * @brief 名前を取得する
     * @return 名前への参照
     */
    const std::string& getName() const
    {
        return name_;
    }

    /**
     * @brief 電話番号を取得する
     * @return 電話番号への参照
     */
    const std::string& getPhone() const
    {
        return phone_;
    }
};


// Person を出力する演算子オーバーロード
std::ostream& operator<<(std::ostream& os, const Person& p)
{
    return (os << '(' << p.getName() << ',' << p.getPhone() << ')');
}

/**
 * @brief Person をコンソール出力する(null 安全)
 * @param person 出力対象の unique_ptr
 */
void coutPerson(const std::unique_ptr<Person>& person)
{
    if (person) {
        std::cout << *person << std::endl;
    } else {
        std::cout << "(null)" << std::endl;
    }
}

/**
 * @brief unique_ptr が指す Person を差し替える(所有権は引数で渡された参照側に残る)
 * @param person 差し替え対象の unique_ptr への参照
 */
void replacePerson(std::unique_ptr<Person>& person)
{
    person = std::make_unique<Person>("bar_2", "090-****-????");
}

int main()
{
    // unique_ptr 生成
    std::unique_ptr<Person> foo = std::make_unique<Person>("foo", "090-****-0123");
    std::unique_ptr<Person> bar = std::make_unique<Person>("bar", "090-****-5555");
    std::unique_ptr<Person> hoge;

    // コンソール出力
    coutPerson(foo);        // (foo,090-****-0123)
    coutPerson(bar);        // (bar,090-****-5555)
    coutPerson(hoge);       // (null)

    // Person 差し替え
    replacePerson(bar);
    coutPerson(bar);        // (bar_2,090-****-????)

    // 所有権の移動
    hoge = std::move(foo);
    // hoge = foo;          // これはコンパイルエラーになる
    coutPerson(foo);        // (null)
    coutPerson(hoge);       // (foo,090-****-0123)

    return EXIT_SUCCESS;
}

 

ビルド(例):

$ g++ -Wall -g make_unique.cpp -o make_unique.out

 

実行結果:

$ ./make_unique.out 
Person::Person()
Person::Person()
(foo,090-****-0123)
(bar,090-****-5555)
(null)
Person::Person()
Person::~Person()
(bar_2,090-****-????)
(null)
(foo,090-****-0123)
Person::~Person()
Person::~Person()
$ 

 

3箇所でクラス Person のデストラクタが実行されている("Person::~Person()" の部分)ことから、unique_ptr が期待通りに解放処理を行っていることがわかります。

 

 

2. 関数の引数に使う

std::unique_ptr を関数の引数に使用する例を紹介します。

 

2-1. その1

関数の引数に unique_ptr を渡してみます。

関数に unique_ptr を渡したり、関数の中で unique_ptr の値を変更したり、といったことができます。

 

[ソースコード: "function_argument_sample.cpp"]

#include <iostream> // for std::cout
#include <memory>   // for std::unique_ptr
#include <string>   // for std::string

/**
 * @brief 簡易的な Person クラス(サンプル用)
 */
class Person {
private:
    std::string name_;
    std::string phone_;

public:
    Person(const std::string& name, const std::string& phone)
        : name_(name), phone_(phone) {}

    const std::string& getName() const { return name_; }
    const std::string& getPhone() const { return phone_; }
};

/**
 * @brief unique_ptr を読み取り専用で受け取り内容を表示する
 * @param person コンテンツを参照する const 参照の unique_ptr
 */
void inspect(const std::unique_ptr<Person>& person)
{
    if (person) {
        std::cout << "inspect: " << person->getName() << ", " << person->getPhone() << "\n";
    } else {
        std::cout << "inspect: (null)\n";
    }
}

/**
 * @brief unique_ptr の参照を受け取り差し替える(所有権は呼び出し元に残る)
 * @param person 差し替える対象の unique_ptr への参照
 */
void resetByRef(std::unique_ptr<Person>& person)
{
    person = std::make_unique<Person>("replaced", "000-0000-0000");
}

/**
 * @brief 値渡しで unique_ptr を受け取り所有権を取得する
 * @param person 所有権を移動して受け取る unique_ptr
 */
void takeOwnership(std::unique_ptr<Person> person)
{
    if (person) {
        std::cout << "takeOwnership: received " << person->getName() << "\n";
    } else {
        std::cout << "takeOwnership: (null)\n";
    }
    // person はここでスコープを抜けると破棄される
}

/**
 * @brief Person を生成して所有権を返すファクトリ関数
 * @param name 氏名
 * @param phone 電話番号
 * @return 所有権を持つ unique_ptr<Person>
 */
std::unique_ptr<Person> makePerson(const std::string& name, const std::string& phone)
{
    return std::make_unique<Person>(name, phone);
}

int main()
{
    std::cout << "-- unique_ptr function argument sample start --\n";

    // 1) makePerson で生成して戻り値で受け取る
    auto p1 = makePerson("alice", "111-1111-1111");
    inspect(p1);

    // 2) resetByRef で参照を渡して差し替える
    resetByRef(p1);
    inspect(p1);

    // 3) takeOwnership に値渡しで渡す(move を使う)
    auto p2 = makePerson("bob", "222-2222-2222");
    takeOwnership(std::move(p2));
    inspect(p2); // p2 は null になっている

    // 4) inspect に null を渡す
    std::unique_ptr<Person> empty;
    inspect(empty);

    std::cout << "-- unique_ptr function argument sample end --\n";
    return 0;
}

 

ビルド方法(g++):

g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic function_argument_sample.cpp -o function_argument_sample.out

 

実行結果:

$ ./function_argument_sample.out 
-- unique_ptr function argument sample start --
inspect: alice, 111-1111-1111
inspect: replaced, 000-0000-0000
takeOwnership: received bob
inspect: (null)
inspect: (null)
-- unique_ptr function argument sample end --
$ 

 

 

3. unique_ptr で動的配列を使う

unique_ptr で動的配列を使用する例です。
T[]の特殊化が組み込まれており、[]演算子も実装されています。直観的で気持ちよく使えます。
ただし範囲外アクセスに対するチェック機構はありません。配列を使用したい場合、まずはコンテナの使用を検討するべきです。時々実行速度を問題にコンテナの使用を止めている場合がありますが、C++11で採用された std::array を使用すれば処理速度の問題もありません。

#include <iostream>         // cout, endl, EXIT_SUCCESS
#include <string>           // string
#include <memory>           // unique_ptr

using namespace std;

int main(int argc, char* argv[])
{
    // std::unique_ptr の例
    // default_delete の特殊化宣言は不要。delete[]が実行される。
    // []演算子もある。
    size_t  n = 10;
    std::unique_ptr<int[]> data(new int[n]);

    cout << "std::unique_ptr<int> : ";
    for (size_t i = 0; i < n; ++i){
        data[i] = i;
    }
    for (size_t i = 0; i < n; ++i){
        cout << data[i] << " ";
    }
    cout << endl;

    return EXIT_SUCCESS;
}

 

 

4. malloc, free を unique_ptr に置き換える

c 言語で規定されている malloc, calloc でメモリを動的に取得した場合、解放に使用する関数は delete や delete[] ではなく free を使用します。デアロケータを指定することで unique_ptr のメモリ解放を free で行えるようにします。

#include <iostream>     // cout, EXIT_SUCCESS
#include <memory>       // shared_ptr, unique_ptr
#include <cstdlib>      // malloc, free

using namespace std;

int main()
{
    const size_t    n = 256;

    // unique_ptr<int[]>
    std::unique_ptr<int[], decltype(&free)> memory(static_cast<int*> (malloc(n*sizeof(int))), std::free);    // "decltype(&free)" は "void(*)(void*)" となるみたい
    // std::unique_ptr<int[], void(*)(void*)> memory(static_cast<int*> (malloc(n*sizeof(int))), std::free);  // だからこちらでもOK

    if (memory.get()==nullptr){
        // malloc失敗
    }
    else{
        // malloc成功
        for (size_t i = 0; i < n; ++i){
            memory[i] = (int)i;
        }
        for (size_t i = 0; i < n; ++i){
            cout << memory[i] << ", ";
        }
        cout << endl;
    }

    return EXIT_SUCCESS;
}

 

 

5. fopen, fclose を unique_ptr に置き換える

これは実装例をいくつか書いてみます。しっくりくるやつを使ってみてください。

5-1. シンプルな実装例

シンプルにやるとこんな感じでしょうか。

#include <stdio.h>  // fopen, fclose
#include <stdlib.h>
#include <memory>   // unique_ptr

#pragma warning(disable : 4996) // fopen, fclose の使用がエラーになるため

void custom_fclose(FILE* fp){
    if (fp != nullptr){
        fclose(fp);
    }
}

int main(int argc, char* argv[])
{
    std::unique_ptr<FILE, decltype(&custom_fclose)>   fp(fopen("fopen_test.txt", "w"), custom_fclose);
    // std::unique_ptr<FILE, void(*)(FILE*)>   fp(fopen("fopen_test.txt", "w"), custom_fclose);

    if (fp.get() != nullptr){
        // fopen成功
        fprintf(fp.get(), "test\n");
    }

    return EXIT_SUCCESS;
    // ここで custom_fclose が自動実行されます
}

 

 

5-2. Lambda式 による実装例

Lambda式 で記載するとこんな感じでしょうか。

#include <stdio.h>          // FILE, fopen, fclose
#include <stdlib.h>
#include <memory>           // unique_ptr

#pragma warning(disable : 4996)

int main(int argc, char* argv[])
{
    std::unique_ptr<FILE, void(*)(FILE*)>   fp(fopen("fopen_test.txt", "w"), [&](FILE* fp){
        if (fp != nullptr){
            fclose(fp);
        }
    });

    // 一応ファイルオープンの成功/失敗を確認してからファイルを使用
    if (fp.get() != nullptr){
        // fopen成功
        fprintf(fp.get(), "test\n");
    }

    return EXIT_SUCCESS;
    // ここで custom_fclose が自動実行されます
}

 

 

5-3. ファクトリーパターン による実装例

ファクトリーパターン的に unique_ptr を返す関数を作成するならばこんな感じでしょうか。

#include <cstdio>       // fopen, fclose
#include <iostream>     // cout, EXIT_SUCCESS
#include <memory>       // unique_ptr

using namespace std;

#pragma warning(disable : 4996)

std::unique_ptr<std::FILE, void(*)(FILE*)> make_file(const char * filename, const char * flags)
{
    // fclose に NULL を渡して実行してはいけない。
    // このため下記のような分岐処理が必要となる。
    std::FILE * const fp = std::fopen(filename, flags);
    return fp ? std::unique_ptr<std::FILE, void(*)(FILE*)>(fp, std::fclose) : std::unique_ptr<std::FILE, void(*)(FILE*)>();
}

int main()
{
    auto fp = make_file("hello.txt", "wb");

    // fp.get() をチェック
    // fopen に失敗していたら fp.get() は NULL
    if (fp.get()){
        fprintf(fp.get(), "Hello world.");
    }
}

私的には Lambda式 を使った2番目のやつが一番シンプルで好きかな。

 

 

6. make_unique

c++14 から make_unique が加わりました。unique_ptr 直接使用せず make_unique によるコーディングが推奨されています。

 

[ソースコード: "make_shared_01.cpp"]

#include <iostream>     // for std::cout
#include <new>          // for std::nothrow, placement new
#include <memory>       // for std::make_unique

/**
 * @brief std::make_shared のシンプルなサンプル
 */

int main() {
    std::cout << "-- make_unique simple examples start --\n";

    // 1) 単一 int 生成、std::make_unique 版
    {
        auto up = std::make_unique<int>();
        std::cout << "make_unique owns = " << *up << "\n";
        // delete は自動的に行われる
    }

    // 2) 単一 int 生成、std::make_unique 版
    {
        auto up = std::make_unique<int>(77);
        std::cout << "make_unique owns = " << *up << "\n";
        // delete は自動的に行われる
    }

    std::cout << "-- make_unique simple examples end --\n";
   return 0;
}

 

ビルド方法:

g++ -std=c++20 -g -Wall -Wextra -Wpedantic make_unique_01.cpp -o make_unique_01.out

 

実行結果:

$ ./make_shared_01.out 
-- shared_ptr simple examples start --
shared_ptr owns = 0
shared_ptr owns = 77
-- shared_ptr simple examples end --
$ 

 

 

class インスタンスの生成もやってみます。

 

[ソースコード: "make_unique_02.cpp"]

#include <iostream>     // cout, endl
#include <string>       // string
#include <memory>       // unique_ptr
#include <cstdlib>      // EXIT_SUCCESS

class Person {
private:
    std::string name_;
    std::string phone_;

public:
    /**
     * @brief デフォルトコンストラクタ
     *        空の名前と電話番号で委譲コンストラクタを呼ぶ
     */
    Person()
        : Person("", "")                // 委譲コンストラクタ
    {
    }

    /**
     * @brief コピーコンストラクタ
     * @param rhs コピー元
     */
    Person(const Person& rhs)
        : Person(rhs.name_, rhs.phone_)
    {
    }

    /**
     * @brief 値を指定するコンストラクタ
     * @param name 氏名
     * @param phone 電話番号
     */
    Person(std::string name, std::string phone)
        : name_(std::move(name)), phone_(std::move(phone))
    {
        std::cout << "Person::Person()" << std::endl;
    }

    /**
     * @brief デストラクタ
     */
    virtual ~Person()
    {
        std::cout << "Person::~Person()" << std::endl;
    }

    /**
     * @brief 名前を取得する
     * @return 名前への参照
     */
    const std::string& getName() const
    {
        return name_;
    }

    /**
     * @brief 電話番号を取得する
     * @return 電話番号への参照
     */
    const std::string& getPhone() const
    {
        return phone_;
    }
};


// Person を出力する演算子オーバーロード
std::ostream& operator<<(std::ostream& os, const Person& p)
{
    return (os << '(' << p.getName() << ',' << p.getPhone() << ')');
}

/**
 * @brief Person をコンソール出力する(null 安全)
 * @param person 出力対象の unique_ptr
 */
void coutPerson(const std::unique_ptr<Person>& person)
{
    if (person) {
        std::cout << *person << std::endl;
    } else {
        std::cout << "(null)" << std::endl;
    }
}

/**
 * @brief unique_ptr が指す Person を差し替える(所有権は引数で渡された参照側に残る)
 * @param person 差し替え対象の unique_ptr への参照
 */
void replacePerson(std::unique_ptr<Person>& person)
{
    person = std::make_unique<Person>("bar_2", "090-****-????");
}

int main()
{
    // unique_ptr 生成
    std::unique_ptr<Person> foo = std::make_unique<Person>("foo", "090-****-0123");
    std::unique_ptr<Person> bar = std::make_unique<Person>("bar", "090-****-5555");
    std::unique_ptr<Person> hoge;

    // コンソール出力
    coutPerson(foo);        // (foo,090-****-0123)
    coutPerson(bar);        // (bar,090-****-5555)
    coutPerson(hoge);       // (null)

    // Person 差し替え
    replacePerson(bar);
    coutPerson(bar);        // (bar_2,090-****-????)

    // 所有権の移動
    hoge = std::move(foo);
    // hoge = foo;          // これはコンパイルエラーになる
    coutPerson(foo);        // (null)
    coutPerson(hoge);       // (foo,090-****-0123)

    return EXIT_SUCCESS;
}

 

ビルド(例):

$ g++ -Wall -Wextra -Wpedantic -g make_unique_02.cpp -o make_unique_02.out

 

実行結果:

$ ./make_unique_02.out 
Person::Person()
Person::Person()
(foo,090-****-0123)
(bar,090-****-5555)
(null)
Person::Person()
Person::~Person()
(bar_2,090-****-????)
(null)
(foo,090-****-0123)
Person::~Person()
Person::~Person()
$ 

 

 

7. make_unique 配列版

c++14 で追加された make_unique は T[] および T[N] に対応しています。
unique_ptr を使用する場合、いろいろな理由から make_unique を使用することが推奨みたいです。

 

 

7-1. 1次元配列

1次元配列を扱う例を記載します。

 

[ソースコード: "make_unique_03.cpp"]

#include <iostream>     // for std::cout
#include <new>          // for std::nothrow, placement new
#include <memory>       // for std::make_shared

/**
 * @brief std::shared_ptr, std::make_shared のシンプルなサンプル
 */

int main() {
    std::cout << "-- make_unique simple examples start --\n";

    // 1) 配列 int 生成、std::make_shared 配列版(C++20 以降)
    {
        const size_t array_size = 5;
        auto arr_up = std::make_unique<int[]>(array_size);   // 要素はデフォルト初期化(ゼロでない場合あり)
        for (size_t i = 0; i < array_size; ++i) {
            arr_up[i] = (i + 1) * 10;
        }

        std::cout << "make_shared<int[]> contents: ";
        for (size_t i = 0; i < array_size; ++i) {
            std::cout << ' ' << arr_up[i];
        }
        std::cout << '\n';
        // delete[] は自動的に行われる
    }

    std::cout << "-- make_unique simple examples end --\n";
   return 0;
}

 

ビルド例:

$ g++ -Wall -Wextra -Wpedantic -g make_unique_03.cpp -o make_unique_03.out

 

実行結果:

$ ./make_unique_03.out
-- make_unique simple examples start --
make_shared contents:  10 20 30 40 50
-- make_unique simple examples end --$ 

 

 

class インスタンスを make_unique で1次元配列にする例も作成してみましょう。

 

[ソースコード: "make_unique_04.cpp"]

#include <iostream>     // for std::cout
#include <memory>       // for std::make_unique
#include <string>
#include <cstdlib>      // for EXIT_SUCCESS

// class Person
class Person {
private:
    std::string  name_;
    std::string  phone_;

public:
    Person()
        : Person("", "")                    // 委譲コンストラクタ
    {
    }
    
    // コピーコンストラクタ
    Person(const Person& rhs)               // コピーコンストラクタ
        : Person(rhs.name_, rhs.phone_)     // 委譲コンストラクタ
    {
    }
    
    Person(const std::string& name, const std::string& phone)
        : name_(name), phone_(phone)
    {
        std::cout << "Person::Person(), name_ = " << name_ << std::endl;
    }

    // デストラクタ
    virtual ~Person()
    {
        std::cout << "Person::~Person(), name_ = " << name_ << std::endl;
    }

    /**
     * @brief コピー代入演算子
     * @param rhs コピー元のPersonオブジェクト
     * @return Person& このオブジェクトへの参照
     */
    Person& operator=(const Person& rhs) {
        name_ = rhs.name_;
        phone_ = rhs.phone_;
        std::cout << "copy operator=, name_ = " << name_ << std::endl;

        return *this;
    }

    /**
     * @brief ムーブ代入演算子
     * @param rhs ムーブ元のPersonオブジェクト
     * @return Person& このオブジェクトへの参照
     */
    Person& operator=(Person&& rhs) noexcept {
        if (this != &rhs) {
            name_ = std::move(rhs.name_);
            phone_ = std::move(rhs.phone_);
            std::cout << "move operator=, name_ = " << name_ << std::endl;
        }
        return *this;
    }
    /**
     * @brief 名前を参照で取得
     * @return const std::string& 名前の参照
     */
    const std::string& getName() const
    {
        return name_;
    }
    /**
     * @brief 電話番号を参照で取得
     * @return const std::string& 電話番号の参照
     */
    const std::string& getPhone() const
    {
        return phone_;
    }
};

// グローバルな演算子オーバーロード */
std::ostream& operator<<(std::ostream& os, const Person& rhs)
{
    return (os << '(' << rhs.getName() << ',' << rhs.getPhone() << ')');
}

// Person をコンソール出力
void PrintPerson(const Person& person)
{
    std::cout << person << std::endl;
}

int main()
{
    const size_t    size_n = 4;
    std::unique_ptr<Person[]> persons = std::make_unique<Person[]>(size_n);

    auto zoo = Person("Zoo", "080-****-9999");

    persons[0] = zoo;
    persons[1] = Person("foo", "090-****-0123");
    persons[2] = Person("bar", "090-****-5555");
    persons[3] = Person("baz", "090-****-6666");


    for (size_t i = 0; i < size_n; ++i) {
        PrintPerson(persons[i]);
    }

    return EXIT_SUCCESS;
}

 

ビルド例:

$ g++ -Wall -Wextra -Wpedantic -g make_unique_04.cpp -o make_unique_04.out

 

実行結果:

$ ./make_unique_04.out 
Person::Person(), name_ = 
Person::Person(), name_ = 
Person::Person(), name_ = 
Person::Person(), name_ = 
Person::Person(), name_ = Zoo
copy operator=, name_ = Zoo
Person::Person(), name_ = foo
move operator=, name_ = foo
Person::~Person(), name_ = 
Person::Person(), name_ = bar
move operator=, name_ = bar
Person::~Person(), name_ = 
Person::Person(), name_ = baz
move operator=, name_ = baz
Person::~Person(), name_ = 
(Zoo,080-****-9999)
(foo,090-****-0123)
(bar,090-****-5555)
(baz,090-****-6666)
Person::~Person(), name_ = Zoo
Person::~Person(), name_ = baz
Person::~Person(), name_ = bar
Person::~Person(), name_ = foo
Person::~Person(), name_ = Zoo
$

 

無駄なコンストラクタ、デストラクタ、の発生が気になりますが、この動作で正しいです。

 

 

7-2. 2次元配列

2次元配列を扱う例を記載します。

 

[ソースコード: "make_unique_05.cpp"]

/**
 * @brief std::unique_ptr と std::make_unique を使った2次元配列のサンプル
 *
 * このサンプルでは、std::unique_ptr を使って動的に2次元配列を作成し、
 * 各要素に値を代入して表示します。
 */

#include <iostream>
#include <memory>

int main() {
    const size_t rows = 3;
    const size_t cols = 4;

    // 行ごとに unique_ptr<int[]> を持つ unique_ptr の配列を作成
    auto matrix = std::make_unique<std::unique_ptr<int[]>[]>(rows);

    for (size_t i = 0; i < rows; ++i) {
        matrix[i] = std::make_unique<int[]>(cols);
    }

    // 値の代入と表示
    for (size_t i = 0; i < rows; ++i) {
        for (size_t j = 0; j < cols; ++j) {
            matrix[i][j] = static_cast<int>(i * cols + j);
            std::cout << matrix[i][j] << " ";
        }
        std::cout << "\n";
    }

    return 0;
}

 

ビルド例:

$ g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic make_unique_05.cpp -o make_unique_05.out
$ 

 

実行結果:

$ ./make_unique_05.out 
0 1 2 3 
4 5 6 7 
8 9 10 11 
$ 

 

 

7-3. 3次元配列

3次元配列を扱う例を記載します。

[ソースコード: "make_unique_06.cpp"]

/**
 * @brief std::unique_ptr と std::make_unique を使った3次元配列のサンプル
 *
 * このサンプルでは、std::unique_ptr を使って動的に3次元配列を作成し、
 * 各要素に値を代入して表示します。
 */

#include <iostream>
#include <memory>

int main() {
    const size_t x = 2;
    const size_t y = 3;
    const size_t z = 4;

    // 3次元配列: unique_ptr<unique_ptr<unique_ptr<int[]>[]>[]>
    auto array3D = std::make_unique<std::unique_ptr<std::unique_ptr<int[]>[]>[]>(x);

    for (size_t i = 0; i < x; ++i) {
        array3D[i] = std::make_unique<std::unique_ptr<int[]>[]>(y);
        for (size_t j = 0; j < y; ++j) {
            array3D[i][j] = std::make_unique<int[]>(z);
        }
    }

    // 値の代入と表示
    for (size_t i = 0; i < x; ++i) {
        for (size_t j = 0; j < y; ++j) {
            for (size_t k = 0; k < z; ++k) {
                array3D[i][j][k] = static_cast<int>(i * 100 + j * 10 + k);
                std::cout << array3D[i][j][k] << " ";
            }
            std::cout << "\n";
        }
        std::cout << "---\n";
    }

    return 0;
}

 

ビルド例:

$ g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic make_unique_06.cpp -o make_unique_06.out
$ 

 

実行例:

$ ./make_unique_06.out 
0 1 2 3 
10 11 12 13 
20 21 22 23 
---
100 101 102 103 
110 111 112 113 
120 121 122 123 
---
$ 

 

 

 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

The MIT License (MIT)

Copyright © 2014 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

変更履歴

2025-11-29 - "2._関数の引数に使う" を追加
2025-11-22 - "7._make_unique_配列版" を追加
2025-11-10 - "1._unique_ptr_を使ってみる", "6._make_unique" を更新
2022-07-13 - ページデザイン更新
2019-11-02 - 「6. make_unique 配列版」を追加
2019-10-26 - 「5. make_unique」を追加
2014-03-09 - 新規作成

 

Programming Items トップページ

プライバシーポリシー