メモリ領域へアクセスする
初期化の話が済んだら、それをどう使うかという話ですね。unique_ptrとかmake_uniqueとか難しいなぁ、と思っている読者はここで安堵するような話になりますよ。
メモリ領域へアクセス[C++11]
スマートポインタを使ったメモリ領域へのアクセスは簡単です。そういえば、普通のポインタでメモリ領域へアクセするするにはどうしていましたっけ……? そう、間接参照演算子いわゆるアスタ(*)や、アロー演算子(->)を使っていましたね。これらをそのまま使って、メモリ領域へアクセスできます。
auto u_int_ptr = make_unique<int>(99);
auto s_str_ptr = make_shared<string>("shared");
// operator *()で参照できる
cout << *u_int_ptr << endl; // 99
cout << *s_str_ptr << endl; // shared
// operator ->()で、size関数を呼び出せる
auto str_size = s_str_ptr->size();
cout << str_size << endl; // 6
2つのポインタは、それぞれint型、string型のスマートポインタです。「*」で参照すれば、整数、文字列の内容がそれぞれ出力されます。「->」で参照すれば、この例のようにstring型のsize関数で文字列長を取得し、出力します。なお、初期化の節で紹介したサンプルには、配布するものには「*」による参照とcoutによる出力が含まれていますので、ぜひ実行して試してください。
[NOTE]演算子のオーバーロードはとても強力
スマートポインタはあくまでもクラスなので、C/C++の生のポインタに対して使うはずの「*」や「->」はそのままでは使えません。しかし、C++には演算子のオーバーロードという他言語でも備えているのは一握りという強力な機能があるのです。この機能により、演算子が適用されたときの動きをカスタマイズできるのです。例えば、「*」に対してはoperator *()という静的関数が呼び出され、そこに記述されている内容が実行されます。スマートポインタという機能で、かつてのポインタと同様の記法を使えるのは、演算子のオーバーロード機能のおかげなのです。
配列へのアクセス[C++11][C++17]
スマートポインタが配列であるときもアクセスは簡単です。配列の要素へのアクセスは、[]演算子を使っていました。これをそのまま使えます。C/C++では、生のポインタを使って*(p+10)とかいう式で配列要素へのアクセスをしていた者もいたようですが(筆者です)、スマートポインタを生ポインタのように見なす演算は定義されていませんので(ただし、get関数を使うと中のポインタを取得できる)、[]を使ってアクセスするのが基本です。以下のリストの前半部は前節の「配列の利用」を参照してください。
…略…
// operator [](size_t)を使ってアクセスする
for (int i = 0; i < 10; i++) {
u_int_array_ptr[i] = i;
s_str_array_ptr[i] = to_string(i);
}
for (int i = 0; i < 10; i++) {
cout << u_int_array_ptr[i] << " ";
cout << s_str_array_ptr[i] << endl;
}
これってよくある危ないコードに見えますね。ループ変数の上限を増やしてみたらどうなるでしょうか? これは各自でコードを書き換えて実行してほしいのですが、範囲外の要素からは、なんと0や空文字列が返ってきます。変な値が返ってきたり、プログラムが落ちたりしません。これは、範囲外の要素へのアクセスは、自動的に配列が拡張されるからですね。ですので、参照ではなく代入にしてやると、しっかりと配列が拡張されて値が代入されます。これはこれで、ロジックとしては誤っているのに、なぜか動いてしまうということにもなってしまいますが……。
メモリ領域の所有権を確認[C++11]
スマートポインタを利用する上で、実際にメモリ領域への所有権があるかというのは重要です。「アンタ、本当に持っているんだろうね?」と疑惑を向けられたときにそれを証明する方法を知っておきましょう。
これはすごく簡単で、スマートポインタのインスタンスをbool型のコンテキストで評価するだけです。持っていればtrue、持っていなければfalseとなります。bool型のコンテキストって?ということなのですが、もっと簡単なのはキャストなどによるbool型としての評価でしょう。このほか、if文の条件式で評価しても同様です。
auto u_int_ptr = make_unique<int>(100);
auto s_str_ptr = make_shared<string>("hello");
// bool型としての評価で所有権の有無を確認できる
bool is_own_i = (bool)u_int_ptr;
cout << "is own int:" << is_own_i << endl;
// if文の条件式にも使える
if (s_str_ptr) {
cout << "s_int_ptr has memory." << endl;
} else {
cout << "s_int_ptr does not have memory." << endl;
}
