はじめに
C言語から派生したオブジェクト指向プログラミング言語であるC++は、21世紀に入ってまったく別物とも言えるプログラミング言語に成長していきました。それは、Modern C++と称されています。1990年代にC++を触っていたプログラマが現在の仕様を知れば、隔世感に苛まれるのではないでしょうか。本連載では、かつてはC++をたしなんでいたという方、今からC++言語を始めるという方に向けて、Modern C++らしい言語仕様をピックアップし紹介していくことで、今のC++言語の姿を理解していただきます。
対象読者
- かつてはC++をたしなんでいたという方
- 今からC++言語を始めるという方
- モダンなプログラミング言語のパラダイムに興味のある方
必要な環境
本記事のサンプルコードは、以下の環境で動作を確認しています。
-
macOS Ventura/Windows 10(64bit)
- Xcode Command Line Tools 2395
- MinGW GCC 9.2.0
スマートポインタって何?
20世紀のC++プログラマ(筆者)が、今(21世紀)のC++を知って驚く連載の第3回です。今回は、スマートポインタです。第1回では「生のポインタは使わないで!」と、C言語やC++言語を使いこなす上での関門的な機能を頭から否定するようなことを書いてしまいましたが、「それなら仕方ないな」「スマートポインタすごいじゃないか」と納得感のあるようにスマートポインタを紹介するのが今回の目的です。
[NOTE]C++プログラムのコンパイル
以降のサンプルは、基本的に「gcc -o 出力先ファイル名 ソースファイル名」でコンパイルできますが、環境によってはライブラリやC++バージョンの指定が必要になることがあります。コンパイルやリンクでエラーが発生するときには、以下のようにライブラリの指定(-lstdc++)、C++バージョンの指定(-std=c++11)の追加を検討してください。
% gcc -o sample sample.cpp -lstdc++ -std=c++11 // c++14, c++17, c++20など
ポインタのおさらい
まずは、C言語のポインタからおさらいしましょう。今でも初学者(40年前の筆者です)を悩ませているポインタですが、機能としてはメモリ上のどこかを指し示すということ以外にはありません。つまりアドレスです。これのどこが悩ましいのか? それは、大きく以下の2点に集約されると筆者は勝手に思っています。
- 意図する場所をきちんと指すようにするのが難しい
- 動的なメモリ領域の管理が難しいというか面倒
本記事を読むような経験豊かで賢明な読者なら、何を言わんとしているか自明ですね。ポインタはあくまでもアドレスなので、そのアドレスが指す先が常に有効かつ意図した場所であることをプログラマが管理しなければなりません。ポインタは演算や代入が可能なので、なんか「++」とか「-=」とかやったり別のポインタにコピーしていたりしていたら、ワケのわからないことになってしまった、ということにもなりがちです(経験談)。
あと、大きな配列を使いたいとか、そういう場合にはmalloc/freeのペアを使いますが(筆者はcalloc派)、これが結構曲者で、確保と解放をきちんと対で書くことができるとなると、かなりの熟練者ですね。しかもmallocには確保できない場合の判定もあり、しかもfreeは無効なポインタでも受け入れてしまうとか、仕様上どうなの?といった振る舞いもあります。
どっちも、キチンとやれば問題ないさ!というのが正論でしょうが、じゃぁアンタは何でもキチンとできるの?と聞かれればグウとなってしまうことでしょう。プログラミングも人間のやること、できれば失敗のないように言語機能やライブラリでフォローしたいものです。
そこで、スマートポインタです。「スマート」という名の通り、「賢い」ポインタです。正しい場所を指さないポインタ(ダングリングポインタとか)、freeを漏らしちゃったとかいうメモリリークといった、ポインタとメモリに関わる好ましくないことを、できるだけ排除してくれます。
スマートポインタの3つの種類
スマートポインタには、3つの種類があります。どのように違うかというと、「所有権」という仕組みへの関わり方によって使い分けます。所有権とは、簡単にいうとメモリ領域の所有者を明確にして、そのメモリ領域の利用の権利を得る代わりに解放の義務を負うという仕組みです。この所有権の仕組みによって、メモリ領域を安全に使えるというわけです。ここでは、所有権との関わりで3つの種類があるのだな、ということをだけ知っておいておいてください。
- std::unique_ptr:一つのスマートポインタだけが所有権を持つ
- std::shared_ptr:複数のスマートポインタで所有権を共有できる
- std::weak_ptr:shared_ptrの管理するメモリ領域への参照を持つ
今回は、所有権と一対一で対応するスマートポインタであるstd::unique_ptr(以降、名前空間は省略)を中心に、C++のバージョンによるサポート状況を示すためにstd::shared_ptrも補足的に紹介していきます。