八葉の日記

日々、感じたことをまとめる場として利用する

unique_ptrについて

C++ではポインタ管理用にunique_ptrが追加されており、これの使い方と解決できる問題をちょっとまとめ。

生ポインタの問題

生ポインタ(rawポインタ)をそのまま使うということはC言語からずっと行われており、C、C++に慣れた方なら言語もうみんな違和感がなくなっている。
しかし、rawポインタの操作には実はどうしても不明瞭な部分があり、それをプログラマーが解決している。

例えば、

Widget* createWidget(int key)

という関数があったとして、これを利用する部分が、

int function()
{
    Widget* p = createWidget(key);
    if (p != nullptr)

    bool ret = p->show();
    if (ret)
    {
        delete p; 
        return -1;
    }
     
     delete p;
     return 1;
}

となっている。

rawポインタだと実は上記のコードを見るだけでは安全とは断言できなくて、
実は次の点が安全か疑問が残るんです。

解放時にdelete pとしているけど本当に大丈夫?

createWidgetの宣言からわかるのはWidget*だけだから、解放の仕方は次の1~3のパターンがあって、(4は注意すること)
コーディング時にプログラマが解放の仕方を調べて安全にする必要がある。

  1. delete不要。例:戻り値がstaticな領域のポインタ、クラスメンバのポインタ
  2. delete[]。例:配列のポインタを返す
  3. 解放用の関数がある。例:free関数がある
  4. 全パスで一度だけ解放する。(リーク対策、2重開放対策)

unique_ptr

unique_ptrを使用するとcreateWidgetを使用する側は受け取ったポインタの消し方を考える必要がなくなる。

std::unique_ptr<Widget>
createWidget()
{
	std::unique_ptr<Widget> p;
	p.reset(new Widget());
	return p;
}

int main()
{
	auto p = createWidget();
	if (p)
	{
		return -1;
	}

	bool ret = p->show();
	if (ret)
	{
		return -1;
	}

	return 1;
}

unique_ptrはただ一人がrawポインタを所有するという特性を持つため、createWidgetからポインタの所有権を移譲されたため、

  1. delete不要。例:戻り値がstaticな領域のポインタ、クラスメンバのポインタ

 →この場合はそもそも戻り値の型を参照型にすればいいと思う

  1. delete[]。例:配列のポインタを返す

 →解放の仕方はunique_ptrで配列だと勝手にdelete []を呼んでくれる

  1. 解放用の関数がある。例:free関数がある

 →deleterを定義できる

  1. 全パスで一度だけ解放する。(リーク対策、2重開放対策)

 →RAIIになっているので必ずスコープを外れると解放される。ポインタの受け渡しはmoveのため2重開放を起こさない

deleterを定義できる
auto
createWidget()
{
	auto deleter = [](Widget* p)
	{
		//
		Widget::free(p);
	};

	std::unique_ptr<Widget, decltype(deleter)> p(nullptr, deleter);
	p.reset(new Widget());
	return p;
}

int main()
{
	auto p = createWidget();
	if (p)
	{
		return -1;
	}

	bool ret = p->show();
	if (ret)
	{
		return -1;
	}

	return 1;
}