C++のお勉強(3) - ポインタ/スマートポインタ
やりたいこと
ポインタの考え方(特にC++11)を理解し、使いこなせるようにする
ブラウザでのコード実行方法
C++ Shell がおススメ
ポインタ
型* 変数名 = オブジェクトのアドレス
&は変数のアドレスの取得
*は指定したアドレスのデータにアクセス
例1 : アドレスを利用した値の書き換え
#include <iostream> #include <string> using namespace std; int main (){ string name = "doraemon"; string *name_address; name_address = &name; *name_address = "dramichan"; cout << name << endl; // dramichan cout << *name_address << endl; // dramichan return 0; }
例2 : ポインタのポインタ
#include <iostream> using namespace std; int main(){ string name = "atom"; string* name_address = &name; string** name_address_adress = &name_address; *name_address = "uran"; // uran cout << name <<endl; // uran cout << **name_address_adress <<endl; }
スマートポインタ
スマートポインタで管理されるオブジェクトは明示的にdeleteしなくても、使われなくなったら自動的に破棄される
unique_ptr
あるメモリの所有権を持つ unique_ptr
使い方
#include
で使える。 ポインタの保持するメモリにアクセスするには、operator*()や operator->()が使用可能。
unique_ptr
は、コピーは禁止だが、ムーブは可能。 deleterの指定可能。
ポイント
unique_ptrで良いところにshared_ptrは使わない
shared_ptr
同一のメモリの所有権を複数で共有できるようにしたスマートポインタが、shared_ptr
使い方
#include
で使える。 ポインタの保持するメモリにアクセスするには、operator*()や operator->()が使用可能。
コピーもムーブも可能。
deleterの指定可能。
ポイント
shared_ptrの初期化にmake_sharedを使うこと
unique_pointに比べて遅い
循環参照に気をつけて!以下のような状況を指す。
#include <iostream> #include <memory> class Robot { public: Robot() {} ~Robot() {} std::shared_ptr<Robot> hand_ptr; }; int main() { std::shared_ptr<Robot> r1 = std::make_shared<Robot>(); std::shared_ptr<Robot> r2 = std::make_shared<Robot>(); r1->hand_ptr = r2; r2->hand_ptr = r1; return 0; } // pointaが解放されない!
- 循環参照はstd::weak_ptrで解決できる
weak_ptr
循環参照によって生じる問題を防ぐために導入されたスマートポインタで所有権をもたない
使い方例
#include <iostream> #include <memory> class Robot { public: Robot() {} ~Robot() {} std::weak_ptr<Robot> hand_ptr; }; int main() { std::shared_ptr<Robot> r1 = std::make_shared<Robot>(); { std::shared_ptr<Robot> r2 = std::make_shared<Robot>(); r1->hand_ptr = r2; r2->hand_ptr = r1; } // ここでr2が解放 return 0; } // ここでr1が解放
ポイント
- ダグリングポインタ(無効なメモリ領域を指すポインタ)に気をつけること!以下に例を示す。
std::shared_ptr<Robot> robot = std::make_shared<Robot>();
std::weak_ptr<Robot> weak_robot = robot;
// この間に、robotの寿命が切れるとweak_robotはダグリングポインタになってしまう
- ここで監視しているshared_ptrオブジェクトを取得するlock関数を用いることでダグリングポインタの問題に対処できる。例えば以下のようにして不正アクセスを防ぐことができる。
if (auto r = weak_robot.lock()) { // こうすることで不正アクセスを防いだうえで*r に対して処理をかける }
参考 : weak_ptr::lock - cpprefjp C++日本語リファレンス
使い分け
使用頻度としてはunique_ptr > shared_ptr > weak_ptrが理想的
まず unique_ptr
の利用を検討する。 複数で共有する必要のある場合、shared_ptr
を利用する。 循環参照になっていないか確認し、なっている場合weak_ptr
を利用する。
所感
次は右辺値・左辺値・ムーブについて理解したい。