unique_ptr 解答ページ | Programming Place Plus Modern C++編【標準ライブラリ】 第3章

トップページModern C++編 C++編](../../index.html) – 第3章

Modern C++編は作りかけで、更新が停止しています。代わりに、C++14 をベースにして、その他の方針についても見直しを行った、新C++編を作成しています。
Modern C++編は削除される予定です。

問題①

問題① std::unique_ptr がコピーできないことを確かめてください。また、コピーができない理由を説明してください。


コピーができないのは、コピーコンストラクタとコピー代入演算子が定義されていないからです。ムーブコンストラクタやムーブ演算子が明示的に定義されているため、コピーに関する関数が自動的に作られることはありません(【言語解説】第13章)。

以下のように、実際にコピーコンストラクタとコピー演算子を使おうとするとコンパイルエラーになります。

#include <memory>

int main()
{
    std::unique_ptr<int> p1(new int(100));

    std::unique_ptr<int> p2(p1);  // コンパイルエラー
    
    std::unique_ptr<int> p3;
    p3 = p1;                      // コンパイルエラー
}

もしコピーができてしまうと、あるリソースを管理する std::unique_ptr が2個以上できてしまいます。すると、最初に破棄された std::unique_ptr がそのリソースを解放してしまい、他の std::unique_ptr は解放済みのリソースを指すポインタを保持し続けることになります。

その後、他の std::unique_ptr が破棄されるとき、すでに解放済みであるとは知らず、再びリソースの解放を行ってしまいます。

std::unique_ptr は、リソースを独占的に管理する仕組みであるという点をよく理解してください。

問題②

問題② 次のコードを std::unique_ptr を使った形に書き換えてください。Create関数は生成の具体的な処理を隠し、抽象化する目的で存在している関数であり、引き続き使用しなければならないものとします。

class MyClass {};

MyClass* Create()
{
    return new MyClass();
}

int main()
{
    MyClass* p = Create();
}


Create関数が std::unique_ptr を作って返すのが良いです。

#include <memory>

class MyClass {};

std::unique_ptr<MyClass> Create()
{
    return std::unique_ptr<MyClass>(new MyClass());
}

int main()
{
    std::unique_ptr<MyClass> p = Create();
}

Create関数が返した std::unique_ptr の一時オブジェクトを、ローカル変数 p を定義するときに使用しています。このとき使っているコンストラクタはムーブコンストラクタです(一時オブジェクトは右辺値なので)。

もし、Create関数が返した std::unique_ptrオブジェクトを受け取らなかったとしても、問題がないことも注目すべきでしょう。戻り値として返される std::unique_ptr の一時オブジェクトは誰にも捕捉されず消滅しますから、そのときに std::unique_ptr のデストラクタが呼ばれて、new で確保されたメモリはきちんと解放されます。

問題③

問題③ 実引数がヌルポインタかどうかを判定して、標準出力へ結果を出力する関数を考えます。生のポインタでもスマートポインタでも使えるように、関数を実装してください。

void PrintNullOrNotNull(/* */)
{
    if (/* */) {
        std::cout << "not null" << std::endl;
    }
    else {
        std::cout << "null" << std::endl;
    }
}


この手の関数では、引数は生のポインタにします。仮に、std::unique_ptr型の引数としてしまうと、所有権を移動させなければならなくなってしまうので、適切ではありません。

#include <iostream>
#include <memory>

void PrintNullOrNotNull(const void* p)
{
    if (p) {
        std::cout << "not null" << std::endl;
    }
    else {
        std::cout << "null" << std::endl;
    }
}

int main()
{
    int n = 100;
    int* p1 = &n;
    std::unique_ptr<int> p2(new int(100));

    PrintNullOrNotNull(p1);
    PrintNullOrNotNull(p2.get());
    PrintNullOrNotNull(nullptr);
}

実行結果:

not null
not null
null

std::unique_ptr の getメンバ関数は、こういう場面で活躍します。ここで間違って、releaseメンバ関数を使うと所有権を失ってしまうので注意してください。



参考リンク


更新履歴

’2017/10/11 新規作成。



第3章のメインページへ

Modern C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る