空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

C++のお勉強(3) - ポインタ/スマートポインタ

f:id:robonchu:20190303150234p:plain:w100

やりたいこと

ポインタの考え方(特に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の指定可能。

ポイント

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が理想的

  1. まず unique_ptrの利用を検討する。

  2. 複数で共有する必要のある場合、shared_ptrを利用する。

  3. 循環参照になっていないか確認し、なっている場合weak_ptrを利用する。

所感

次は右辺値・左辺値・ムーブについて理解したい。

全体参考