C++ スマートポインタについて/使い分けるべき3つのスマートポインタとは?
C++から離れて10年ぐらいたつので、ポインタにもこんなに種類が存在してるとは思いませんでした。
生のポインタと3つのスマートポインタの使い分け
ここまで、性質の違う3種のスマートポインタ、
unique_ptr<T>
shared_ptr<T>
weak_ptr<T>
を見てきた。これに加えて、従来の生のポインタも存在する。これらは、どのように使い分ければいいのだろう? この問題を考えるために、ポインタの利用方法における二つの側面に注目してみよう。一つ目は、ポインタの指す対象を 「所有」しているのか、単に 「参照」しているのか、である。所有している、すなわち自身が解放する責任を有する動的確保したメモリを指すためにポインタを使っているのであれば、それは「所有」のポインタである。一方、単にアクセスするためだけにしか使わないのであれば、それは「参照」である。
一般に、あるオブジェクトを「所有」するのは単一のオブジェクトであるが、まれに 複数のオブジェクトが単一のオブジェクトを「所有」するのが自然である場合もある。例えば、複数のperson(人物)で構成されるcommittee(委員会)クラスを考えた場合、committeeはpersonを「所有」するのは自然な設計である。しかし、あるpersonが複数のcommitteeに参加する場合には、複数のcommitteeによって所有権は共有されることになる。
二つ目は、ポインタの指す対象へのアクセスが、アクセス中に解放されることのない 「安全なアクセス」か、解放される可能性がある 「危険なアクセス」か、である。自身が所有するポインタへのアクセスや、所有者より内側のスコープで利用している場合は、基本的に「安全なアクセス」だといえる。一方で、アクセスしようとするメモリが、いつ解放されるか分からない他のオブジェクトが所有権を持つ場合、その利用は「危険なアクセス」であるといえる。
これら二つの側面から、ポインタの使い方をいくつかに分類することができる。あくまで一つの指針としてであるが、以下のような基準で使い分けるとよいのではないか、と筆者は考えている。
- メモリを動的確保によって「所有」する必要がある場合、原則として
unique_ptr<T>
を利用する。
- ただし、メモリへのアクセスは所有者のメンバ関数内に限るなど、「危険なアクセス」による「参照」が必要ない設計にしておく。
- メモリを動的確保によって「所有」するが、以下のいずれかの場合には
shared_ptr<T>
を利用する。
- 複数のオブジェクトによって「所有」されるのが、最も自然な設計である場合。
- 「危険なアクセス」による「参照」が、設計上どうしても必要な場合。
- 「危険なアクセス」による「参照」には、
weak_ptr<T>
を利用する。- 「安全なアクセス」による「参照」か、外部のAPIの利用する際にどうしても必要である場合に限り、生のポインタを使ってよい。それ以外では原則として生ポインタを使ってはいけない。
もちろん、これはあくまで参考となる指針であって、明確なルールとなるものではないと思っている(こんな基準じゃだめだ、もっとこうした方がいい、という意見があれば、ぜひコメント頂けるとありがたいです)。
重要なことは、これまで単に生のポインタで記述していたものを、これら三種のスマートポインタも使って書き分けることができる、ということである。スマートポインタ自体の機能に加えて、プログラマーがどういった意図で使用しているのかをより明確に記述することで、動的メモリの利用が孕む危険性は大きく減らすことができるだろう。