SlideShare a Scribd company logo
1 of 145
Download to read offline
C++
ポインタ ブートキャンプ
                  @hotwatermorning




  Sapporo.cpp&CLR/H 合同勉強会
自己紹介
• @hotwatermorning
• Sapporo.cpp
• DTMer
• 7/7のプロ生勉強会で発表など
サラリーマン100人
    に聞きました。
• 「C++のポインタのイメージ」
サラリーマン100人
    に聞きました。
• 「C++のポインタのイメージ」
 よく分からない
     難しい
  触りたくない
    トラウマ
  俺が規格書だ
ちょうどC, C++の
  ポインタを
学んでいる人や
ポインタ周りの構文
で嵌っている人向け
本日の訓練メニュー
• 第一部 「ポインタの基礎」
• 第二部「ポインタの嵌りどころ」
• 第三部「ポインタを使わない」
第一部



ポインタの基礎
ポインタの基礎


•   ポインタとは、
    何らかのオブジェクトを指す
    オブジェクト
ポインタの基礎


•   ポインタとは、
    何らかのオブジェクトを指す
    オブジェクト
ポインタの基礎


•   オブジェクトとは、
    変数の定義やnew-式などによって
    メモリ上に確保され占有された
    領域のこと
       “オブジェクトはdefinition(3.1), new-式(5.3.4), あるいはimplementation(12.2)によって作成される” (1.8/1)
       “a most derived objectは0ではないサイズを持ち、1byte以上の領域をメモリ上で占有する。” (1.8/5)


       Working Draft, Standard for Programming Language C++ (N3337) より。
int main()
{
  int i;
}

この時、変数iはint型のオブジェクト。
たとえば変数iはメモリ上で0x7fff5fbfea54から
始まる数バイトの領域を占有している。
(アドレスは実行毎に変わりうる)
int main()
{
  int i;
}

この占有しているサイズは型ごとに
固有で、sizeof(type)で取得できる。
intが4バイトの処理系では、
sizeof(int)は4を返し、
変数iは0x7fff5fbfea54から始まる4バイトの領域
を占有する。
・・・
                   0x7fff5fbfea4f
 int main()
                   0x7fff5fbfea50
 {                 0x7fff5fbfea51
   int i;          0x7fff5fbfea52
 }                 0x7fff5fbfea53
                   0x7fff5fbfea54
この占有しているサイズは型ごとに   0x7fff5fbfea55
                          i
                   0x7fff5fbfea56
固有で、sizeof(type)で取得できる。
                   0x7fff5fbfea57
intが4バイトの処理系では、    0x7fff5fbfea58
                   0x7fff5fbfea59
sizeof(int)は4を返し、 0x7fff5fbfea5a
                   0x7fff5fbfea5b
変数iは0x7fff5fbfea54から始まる4バイトの領域
                   0x7fff5fbfea5c
                   0x7fff5fbfea5d
を占有する。             0x7fff5fbfea5e
                      ・・・
int main()
{
  int i;
  i = 3;
}

このプログラムはメモリ上の0x7fff5fbfea54に
int型の3を書き込んでいる
int main()
{
  int i;
  i = 3;
}

変数iの占有する領域(0x7fff5fbfea54)を知ってい
るのは変数iだけ。つまり、0x7fff5fbfea54から
始まる4バイトに対する値の読み書きは、
今のところ変数i経由でしか行えない。
int main()
{
  int i;
  i = 3;
}

この時、変数iからオブジェクトのアドレス
(0x7fff5fbfea54)が取得でき、
アドレスの先にあるオブジェクトに対して、
何バイトかの値を直接読み書きできるような
仕組みがあれば・・・
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
           //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
元になる変数を使わずに、
 ! //その位置にあるオブジェクトを操作する!
アドレス経由でメモリを読み書きして、
 ! assert(i == 3):
 }
オブジェクトを操作できる。
ポインタの基礎

•   ポインタは
    オブジェクトのアドレスを保持す
    るオブジェクト

• ポインタ変数なんて呼ばれたりも
    する
ポインタの基礎

•   ポインタは
    オブジェクトのアドレスを保持す
    るオブジェクト

• アドレスを経由して、アドレスが
    指すオブジェクトを操作できる
ポインタの基礎



• だだし、前ページの擬似コードに
 は欠陥がある。
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
擬似コード
 int main()
 {
     char i;//例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
擬似コード
 int main()
 {
     std::list<int> i;//例えば変数i...
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
擬似コード
 int main()
 {
     MyClass i;//例えば変数i...
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
ポインタの基礎

• 前ページの擬似コードで使用して
 いるAnyPointerはアドレス値を保存
 するだけ。

• アドレスの先にあるオブジェクト
 の型は知らない。
ポインタの基礎


• なんのオブジェクトに対する
 アドレスなのか

• 型ごとにその型のアドレスを扱う
 ためのポインタ
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     AnyPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるオブジェクトを操作する!
 !   assert(i == 3):
 }
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     IntPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
ポインタの基礎

• int型にはIntPointerのような型
• charにはCharPointerのような型
• MyClassにはMyClassPointer(ry
• これがあれば、アドレスからその
 先のオブジェクトを操作できる
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     IntPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
実際のコード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     int *    pi = &i;!
 !   //piの値は0x7fff5fbfea54
     *pi      = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
ポインタの基礎
• 擬似コードと実際に動く
  コードでの文法の対応

• IntPointer → int *
• addressof(i) → &i
• indirect(i) → *i
ポインタの基礎
• 擬似コードでのポインタの型と
 実際に動くコードでの型の対応

• IntPointer → int *
• CharPointer → char *
• MyClassPointer → MyClass *
ポインタの基礎
• ポインタの文法
ポインタの宣言
• T型のオブジェクトへのポインタの
 変数を宣言する際には、
 変数を*(indirection演算子)で修飾す
 る
 T!*!pt;
 // T*!pt; T!*pt; T*pt; とも書ける。
アドレスの取得
• T型の変数tやオブジェクトの左辺
 値があるとき
 &t;

 で、オブジェクトのアドレスを
 取得できる
アドレスの取得
• T型の変数tやオブジェクトの左辺
 値があるとき
 &t;

• 上記のコードで、tに前置している
 単項演算子&はaddress-of演算子と
 いう
アドレスの取得
• T型の変数tやオブジェクトの左辺
 値があるとき
 &t;

• このように、変数に&演算子を
 前置した式は、tのアドレスを保持
 するポインタを返す
ポインタへの代入
• T型の変数tとT型のオブジェクト
 へのポインタptがあるとき、
 pt = &t;

 このようにして、address-of演算子
 が返すポインタを別のポインタに
 代入できる
ポインタの間接参照
• T型のオブジェクトへのポインタpt
 があるとき、
 *pt;

 このようにして、アドレスの先に
 あるオブジェクト(のlvalue)を取得
 できる
ポインタの間接参照
• そのため、取得したオブジェクト
    に対して、
    *pt = *pt + 1;

    このようにして、値を読み書きで
    きる
•   (上記のコードは、ptが指す先のオブジェクトに1を加えている。)
ポインタの間接参照
 *pt;

• このように、ポインタからその
 アドレスの位置にある
 オブジェクトを参照する操作を
 間接参照(indirection)
 という。
ポインタの間接参照
 *pt;

• 上記のコードで、ポインタに前置
 している単項演算子*は
 indirection演算子や
 dereference演算子という
ポインタの間接参照
 *pt;

• このように、ポインタに*演算子を
 前置した式は、ptの指すアドレス
 の位置にあるオブジェクト
 (のlvalue)を返す。
再掲
int main()
{
    int i; //例えば変数iのオブジェクトは
           //0x7fff5fbfea54にいる。
    int *    pi = &i;!
!   //piの値は0x7fff5fbfea54
    *pi      = 3;
!   //piに代入されているアドレス経由で
!   //その位置にあるint型のオブジェクトを
    //操作する!
!   assert(i == 3):
}
メンバアクセスの構文
• ポインタでクラスを扱うときは、
 非ポインタの時とメンバアクセス
 の構文が異なる。
struct Time {
    int hour;
    int minutes;
    int seconds;
};

int main()
{
    Time t;
    t.hour = 6;
    t.minutes = 19;
    t.seconds = 00;
}
struct Time {
    int hour;
    int minutes;
    int seconds;
};

int main()
{
    Time t;
    Time *pt = &t;
    pt->hour = 6;
    pt->minutes = 19;
    pt->seconds = 00;
}
メンバアクセスの構文
• オブジェクトから直接メンバに
 アクセスするときは、
 operator.(ドット演算子)を使用する


t.access_to_member_;
t.invokeMemberFunction();
メンバアクセスの構文
• ポインタから間接参照して
 オブジェクトのメンバにアクセス
 するときは、operator->(アロー演
 算子)を使用する

pt->access_to_member_;
pt->invokeMemberFunction();
newとポインタ
• C++で、動的にオブジェクトを生
 成するには、new演算子を使用す
 る。
int main()
{
    int *pi = new int();
    *pi = 1;
    delete pi;
}
int main()
{
    MyClass *pm = new MyClass();
    *pm = 1;
    delete pm;
}
int main()
 {
     MyClass *pm = new MyClass();
     *pm = 1;
     delete pm;
 }

new-式によるオブジェクトの生成は、
まずメモリ上にその型の領域が確保され、
次にオブジェクトのコンストラクタが
実行され、最後に作成されたオブジェクトへの
ポインタが返る。
int main()
 {
     MyClass *pm = new MyClass();
     *pm = 1;
     delete pm;
 }

先程までの例では変数iなどが、0x7fff5fbfea54の
ようなアドレスにあるオブジェクトを直接
表していたために、変数iによって、0x7fff5fbfea54
の領域を直接読み書きできた。
int main()
 {
     MyClass *pm = new MyClass();
     *pm = 1;
     delete pm;
 }

しかし、new-式によって生成された
オブジェクトは、メモリ上にオブジェクトの
領域は確保されても、直接そのオブジェクトを
指す変数はない。
int main()
 {
     MyClass *pm = new MyClass();
     *pm = 1;
     delete pm;
 }

そのため、new-式から返るポインタ経由で
間接的に、オブジェクトを扱うことになる。
int main()
 {
     MyClass *pm = new MyClass();
     *pm = 1;
     delete pm;
 }

また、new-式によって確保されたメモリ領域は
明示的に解放しない限り、プログラムが終了
するまでメモリ上に残り続ける。
使用する必要がなくなった段階でdelete演算子に
ポインタを渡して解放する必要がある。
第二部



ポインタの嵌りどころ
ポインタの嵌りどころ

• 構文がややこしい
• 多重ポインタ
• constの付加
ポインタの嵌りどころ

• 構文がややこしい
• 多重ポインタ
• constの付加
int main()
{
!   int i     = 3;
!   int *pi   = &i;

    *pi       = *pi + 1;
}
int main()
{
!   int i     = 3;
!   int *pi   = &i;

    *pi       = *pi + 1;
}
色々なところに*piが現れてる!!
int main()
{
!   int i     = 3;
    int *pi   = &i;

    *pi       = *pi + 1;
}
宣言時の構文と変数
• 宣言時に、これから宣言するオブ
 ジェクトがポインタだと指定する
 ために*演算子を使用する。
 int *pi;

• 変数自体はあくまで
 pi;
int main()
{
!   int i     = 3;
    int *pi   = &i;

    *pi       = *pi + 1;
}
int main()
{
!   int i    = 3;
    int *pi;   //ポインタの宣言と
    pi = &i;    //代入を分けて書くと
    *pi        = *pi + 1;
}
int main()
{
!   int i    = 3;
    int *pi;   //ポインタの宣言と
    pi = &i;    //代入を分けて書ける
    *pi        = *pi + 1;
}
int main()
 {
 !   int i    = 3;
     int *pi;   //ポインタの宣言と
     pi = &i;    //代入を分けて書ける
     *pi        = *pi + 1;
 }

            間接参照
(元のオブエジェクト: 変数iを取得する)
ポインタの嵌りどころ

• 構文がややこしい
• 多重ポインタ
• constの付加
int main()
{
    int ** ppi;
}




このpは*演算子が2つ指定されている。
これはどんなオブジェクトか。
次のように書きなおしてみる。
int main()
{
    typedef int * IntPointer;
    IntPointer * ppi;
}

ppiの型はIntPointer型のオブジェクトへの
ポインタだとわかる。
ということは、変数ppiは、
IntPointer型(= int *型)のオブジェクトのアドレス
を保持できるということ。
int main()
{
    int *pi;
    int **ppi;

    ppi = &pi;   //int *型の変数のアドレスを
                 //ppiに代入できる。
}
int main()
{
    int **ppi = get_some_pointer();
!   int *pi = *ppi;
!   //間接参照するとアドレスの先の
!    //int *型のオブジェクトが返る
!    int i = *pi;
!    //もう一度間接参照するとアドレスの先の
!    //int型のオブジェクトが返る
!    int i = **ppi; //2重に間接参照できる
}
    注) ppi, *ppiに有効なオブジェクトへのアドレスが代入されていなければ
    上記のコードは未定義動作を起こし、アクセス違反などでクラッシュする。
ポインタの嵌りどころ

• 構文がややこしい
• 多重ポインタ
• constの付加
まずconstについて
    • 変数をreadonlyにする仕組み
int main()
{
    int const value = get_some_value();
    //型にconstを後置する
     value = 0; //コンパイルエラー
}
まずconstについて
    • 変数をreadonlyにする仕組み
int main()
{
    const int value = get_some_value();
    //constを型に前置する流儀もある
     value = 0; //コンパイルエラー
}
ポインタとconst
• ポインタのconst性には2種類の
 状況がある

• ポインタというオブジェクト
  自体のconst性

• ポインタが指すオブジェクト
  へのconst性
ポインタとconst
      • ポインタというオブジェクト
       自体のconst性
int   main()
{
!     int i = 3;
!     int j = 3;
!     int * const pi = &i;
      //piに変数iのアドレスを設定
!     pi = &j; //コンパイルエラー。
!              //piの値は変更できない。
}
ポインタとconst
      • ポインタが指すオブジェクト
       へのconst性

int   main()
{
!     int i = 3;!
!     int const * pi = &i;
!     *pi = 4; //コンパイルエラー。
!     //constなオブジェクトは変更できない。
}
ポインタとconst
 • この2つの状況を考慮すると、
T       *       p;
T       * const p;
T const *       p;
T const * const p;

    この4種類のconst性が異なる
    ポインタが宣言できる。
ポインタとconst
 • ここでT型のポインタとconst性を
typedef T       * TPointer;
typedef T const   TConst;
typedef TConst * TConstPointer;


   上記のようなtypedefした名前で考
   えてみると
ポインタとconst
 • 宣言はこのようになる
TPointer            p;
TConstPointer       p;
TPointer      const p;
TConstPointer const p;
ポインタとconst
TPointer            p;
 •   変数pはTPointer型のオブジェクト

 •   p自体はconstなオブジェクトではない。
     よって、pの値(保持するアドレス)は変更で
     きる。

 •   *pで取得されるオブジェクトの型は、
     TPointer(= T *)の間接参照なのでT。
     よって、*pで取得できるオブジェクトの値は
     変更できる。
ポインタとconst
TConstPointer         p;
 •   変数pはTConstPointer型のオブジェクト

 •   p自体はconstなオブジェクトではない。
     よって、pの値(保持するアドレス)は変更で
     きる。

 •   *pで取得されるオブジェクトの型は、
     TConstPointer(= T const *)の間接参照なので
     TConst。よって、*pで取得できるオブジェク
     トの値は変更できない。
ポインタとconst
TPointer const      p;
 •   変数pはTPointer型のconstなオブジェクト

 •   p自体はconstなオブジェクトである。
     よって、pの値(保持するアドレス)は変更で
     きない。

 •   *pで取得されるオブジェクトの型は、
     TPointer(= T *)の間接参照なのでT。
     よって、*pで取得できるオブジェクトの値は
     変更できる。
ポインタとconst
TConstPointer const p;
 •   変数pはTConstPointer型のconstなオブジェクト

 •   p自体はconstなオブジェクトである。
     よって、pの値(保持するアドレス)は変更で
     きない。

 •   *pで取得されるオブジェクトの型は、
     TConstPointer(= T const *)の間接参照なので
     TConst。よって、*pで取得できるオブジェク
     トの値は変更できない。
ポインタとconst
• ポインタのconst性まとめ
 • ポインタ自体がconstか
 • ポインタの指すオブジェクトが
 constか

• この二つの組み合わせ
第三部



ポインタを使わない
ポインタの問題点
• 自由に制御でき過ぎる
 • 容易に無効な状態にできる
• 指しているオブジェクトを管理
 していない
ポインタの問題点
• 自由に制御でき過ぎる
 • 容易に無効な状態にできる
• 指しているオブジェクトを管理
 していない
//整数の除算をおこなう関数
void divide(
    int dividend, int divisor,
    int *quotient, int *remainder )
{
    *quotient = dividend / divisor;
    *remainder = dividend % divisor;
}
int main()
{
    int quotient;

    //余りは使わなくていいから
    //nullptr指定しちゃえ☆
    divide(8, 4, &quotient, nullptr);

    std::cout
!    << "8 / 4 = " << quotient
!    << std::endl;
}
int main()
  {
      int quotient;

     //余りは使わなくていいから
     //nullptr指定しちゃえ☆
      divide(8, 4, &quotient, nullptr);

quotientとremainder両方とも指定して欲しいのに、
        std::cout
 !  << "8 / 4 = " << quotient
利用者がnullptrや無効なアドレスを渡せてしまう。
 !  << std::endl;
 }
int main()
{
    int quotient;

   //余りは使わなくていいから
   //nullptr指定しちゃえ☆
    divide(8, 4, &quotient, nullptr);
関数divideの中でnullptrへの書き込みが
  std::cout
!  << "8 / 4 = " << quotient
発生し、プログラムがクラッシュする。
!  << std::endl;
}
参照
• C++で導入された、無効値を
 取らずに何らかのオブジェクト
 を指す仕組み。

• ポインタ的な性質を持ちつつ、
 普通の変数のような構文で使用
 できる。
参照
• ポインタよりもできることが
 制限されているため、より安全
 に使用できる。
int main()
{
    int i = 1;
    int &ri = i; //参照の定義と初期化
    //参照は必ずなんらかの
    //参照元となるオブジェクトを指定して
    //初期化されなければならない。
    ri = 2;     //参照への代入
    assert(i == 2);
}
int main()
{
    int i = 1;
    int &ri = i; //参照の定義と初期化
    //参照は必ずなんらかの
    //参照元となるオブジェクトを指定して
    //初期化されなければならない。
    ri = 2;     //参照への代入
    assert(i == 2);
}
//整数の除算をおこなう関数
//引数の型を参照にした
void divide(
    int dividend, int divisor,
    int &quotient, int &remainder )
{
    quotient = dividend / divisor;
    remainder = dividend % divisor;
    //参照変数へは普通の変数と同じように
    //アクセスできる。
}
int main()
{
    int quotient;
    int remainder;
    //参照引数を取る関数の呼び出し時に
    //オブジェクトを渡す。
    divide(8, 4, quotient, remainder);

    std::cout
!    << "8 / 4 = " << quotient
!    << std::endl;
}
int main()
{
    int quotient;
    int remainder;
    //nullptrのような無効なオブジェクトは
    //表現できないようになっている
    //以下はコンパイルエラー
    divide(8, 4, quotient, nullptr);

    std::cout
!    << "8 / 4 = " << quotient
!    << std::endl;
}
ポインタの参照
• ポインタもアドレスを保持する
 ためのただのオブジェクトなの
 で、ポインタの参照というもの
 も考えられる。
void delete_and_set_null(int *& i)
{
    delete i;
    i = nullptr;
}
int main()
{
    int *pi = new int ();
    *pi = 1;
    delete_and_set_null(pi);
!   //関数にポインタを参照で渡して、
!   //値をdeleteしたあとヌルポインタを代入
!   assert(pi == nullptr);
}
ポインタの参照
• 関数の引数にポインタの参照を
 受け渡した時の働きは、C#で
 言うところのrefキーワードを
 使用したオブジェクトの受け
 渡しに近い。
ポインタの問題点
• 自由に制御でき過ぎる
 • 容易に無効な状態にできる
• 指しているオブジェクトを管理
 していない
オブジェクトの所有権
• ポインタはアドレスを保持して
 いるだけで、その先にあるオブ
 ジェクトの寿命は管理していな
 い。
オブジェクトの所有権
• ダングリングポインタ
• メモリリーク
ダングリングポインタ
• オブジェクトが破棄されたのに
 ポインタのアドレスがそのまま

• そのポインタを間接参照すると
• 無効なオブジェクトへの
 アクセスとなる
void foo()
{
    Data *d = new Data();
    d->getSomeValue();
    delete d; //僕「先に綺麗にしておこう」
     //...その他雑多な処理が沢山
     //...その他雑多な処理が沢山
     //...その他雑多な処理が沢山
     d->getSomeAnotherValue();
!    //僕「最後にあれをやっておこう」
}

    一度解放した領域を間接参照しようとした
void foo()
{
    Data *d = new Data();
    d->getSomeValue();
    delete d; //僕「先に綺麗にしておこう」
    //...その他雑多な処理が沢山
    //...その他雑多な処理が沢山
    //...その他雑多な処理が沢山
    delete d; //僕「関数から抜けるので
!          //ちゃんと後片付けしておこう」
}
一度解放した領域をもう一度解放しようとした
void foo()
{
    Data *d = new Data();
    d->getSomeValue();
    delete d; //僕「先に綺麗にしておこう」
    //...その他雑多な処理が沢山
    ダングリングポインタ
    //...その他雑多な処理が沢山
    //...その他雑多な処理が沢山
    delete d; //僕「関数から抜けるので
!          //ちゃんと後片付けしておこう」
}
一度解放した領域をもう一度解放しようとした
メモリーリーク
• newで動的にメモリを確保し生成
 したオブジェクトのアドレスを
 紛失し、解放できなくなってし
 まうこと。
void foo()
{
    Data *d = new Data();
    d->foo();
    return;
    //おっと!delete d;を忘れている。
}

このまま関数を抜けてしまうと、dのアドレスは
誰も知らなくなる。
dのアドレスの位置にあるオブジェクトは
どうなる?
void foo()
{
    Data *d = new Data();
    d->foo();
    return;
    //おっと!delete d;を忘れている。
}

C++にはGCが無いため、newで確保されたメモリ
領域は、正しく解放しなければ、
プログラム終了時までメモリ上に残ってしまう。
void foo()
{
    Data *d = new Data();
    d->foo();
    return;
    //おっと!delete d;を忘れている。
}     メモリーリーク
C++にはGCが無いため、newで確保されたメモリ
領域は、正しく解放しなければ、
プログラム終了時までメモリ上に残ってしまう。
ポインタでメモリ管理
• C言語では、free後にポインタを
 NULLで初期化して、ポインタが
 無効であることを明示するのが
 一般的なメモリ管理の手法。

• プログラマが常に明示的にポイ
 ンタの有効性を意識する
ポインタでメモリ管理

•   一方C++は
    スマートポインタを
    使った
スマートポインタ
• ポインタをラップしつつ、
 あたかもポインタのように扱え
 るようにしたクラス

• ポインタが指すオブジェクトの
 所有権を適切に管理できる
int main()
{
    {
        std::unique_ptr<Data> data(
            new Data()
        );
        data->doSomething();
    }
}
int main()
 {
       {
           std::unique_ptr<Data> data(
               new Data()
           );
           data->doSomething();
std::unique_ptrクラスのコンストラクタに、
       }
 }
newで生成したオブジェクトのポインタを渡す。
以降は変数dataが、newで生成した
オブジェクトを管理する。
int main()
{
    {
        std::unique_ptr<Data> data(
            new Data()
        );
        data->doSomething();
    }
}
変数dataはポインタのように扱える
int main()
{
    {
        std::unique_ptr<Data> data(
            new Data()
        );
        data->doSomething();
    } //ここでdeleteが呼ばれる
}
スマートポインタの変数の寿命(この例だと
スコープを抜ける時)で自動的に管理している
オブジェクトがdeleteされる。
int main()
{
    {
        std::unique_ptr<Data> data(
            new Data()
        );
        data->doSomething();
このように、オブジェクトの寿命によって
    } //ここでdeleteが呼ばれる
リソース管理を行う手法を
}
RAII(Resource Acquisition Is Initialization)という。
スマートポインタ
• RAIIによって、newで作成したオ
 ブジェクトを変数の寿命として
 管理できる。
スマートポインタ
• RAIIによって、newで作成したオ
 ブジェクトを変数の寿命として
 管理できる。

• 例外安全性も高まる。
•   Exceptional C++

•   「例外安全入門」
int main()
{
    std::unique_ptr<int> pi(new int());

    *pi = 1;

    pi.reset(new int());
    //新しいオブジェクトをセット
    //先に管理していた方は自動でdeleteされる

    pi.reset(); //スマートポインタを初期化
!   //管理しているオブジェクトは
!   //自動でdeleteされる
}
int main()
{
    std::unique_ptr<int> pi(new int());

    *pi = 1;

    pi.reset(new int());
    //新しいオブジェクトをセット
newとdeleteと常に明示的に対応させて管理しな
    //先に管理していた方は自動でdeleteされる
くてもよくなる。
    pi.reset(); //スマートポインタを初期化
!   //管理しているオブジェクトは
!   //自動でdeleteされる
}
スマートポインタ
• 生のポインタより高機能
 • カスタムデリータ
 •   オブジェクトが破棄されるときに行われ
     る処理を指定できる。
スマートポインタ
• 生のポインタより高機能
 • スマートポインタはその種類
 ごとに異なる特徴
スマートポインタ
• std::unique_ptr
 • コピー不可/ムーブ可
 •   newで作成したオブジェクトは
     ただひとつのunique_ptrクラスのインスタン
     スだけで管理される
スマートポインタ
• std::shared_ptr
 • コピー可/ムーブ可
 •   newで作成したオブジェクトは
     複数のshared_ptrのインスタンスから共有さ
     れる。
スマートポインタ
• boost::scoped_ptr
 • コピー不可/ムーブ不可
 •   newで作成したオブジェクトは
     一つのscoped_ptrのインスタンスで管理さ
     れ、そのインスタンスがスコープから抜け
     るときに破棄される。
スマートポインタ
• 生のポインタより高機能
 • スマートポインタはその種類
 ごとに異なる特徴

• これらを使い分けることで、
 コードの意味をより明示でき
 る。
まとめ
• ポインタの構文をおさらい
擬似コード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     IntPointer pi = addressof(i);!
 !   //piの値は0x7fff5fbfea54
     indirect(pi) = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
実際のコード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
            //0x7fff5fbfea54にいる
     int *    pi = &i;!
 !   //piの値は0x7fff5fbfea54
     *pi      = 3;
 !   //piに代入されているアドレス経由で
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
実際のコード
 int main()
 {
     int i; //例えば変数iのオブジェクトは
       宣言 //0x7fff5fbfea54にいる
     int *    pi = &i;!
 !   //piの値は0x7fff5fbfea54
                   address-of
     *pi      = 3;
 !   //piに代入されているアドレス経由で
     間接参照
 !   //その位置にあるint型のオブジェクトを
     //操作する!
 !   assert(i == 3):
 }
まとめ
• ポインタの構文をおさらい
• スマートポインタを使おう。
まとめ
• ポインタの構文をおさらい
• スマートポインタを使おう。
 • メモリ管理の煩わしさを軽減す
 る。

• コードの意味を明確にする。
その他ポインタと絡む話
• ポインタと配列
• ポインタと関数
• メンバ変数のメモリ管理
• ポインタと継承
最後にお知らせ
• 「C++忘年会 2012 in 札幌」
• 12/15日(土)を候補に進めてます
• 興味が有る方は是非ご参加くださ
 い!!
最後にお知らせ
• 「C++忘年会 2012 in 札幌」
• 12/15日(土)を候補に進めてます
• 興味が有る方は是非ご参加くださ
 い!!

More Related Content

What's hot

組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろう
Kota Mizushima
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
Norishige Fukushima
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
Genya Murakami
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会
Akihiko Matuura
 

What's hot (20)

ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろう
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexpr
 
Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)Introduction to Categorical Programming (Revised)
Introduction to Categorical Programming (Revised)
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜
 
不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
 
(公開版)FPGAエクストリームコンピューティング2017
(公開版)FPGAエクストリームコンピューティング2017 (公開版)FPGAエクストリームコンピューティング2017
(公開版)FPGAエクストリームコンピューティング2017
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
 
Scala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみたScala 初心者が米田の補題を Scala で考えてみた
Scala 初心者が米田の補題を Scala で考えてみた
 
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会
 
マーク&スイープ勉強会
マーク&スイープ勉強会マーク&スイープ勉強会
マーク&スイープ勉強会
 

Similar to C++ ポインタ ブートキャンプ

Wrapping a C++ library with Cython
Wrapping a C++ library with CythonWrapping a C++ library with Cython
Wrapping a C++ library with Cython
fuzzysphere
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
kiki utagawa
 
C言語の宣言読み方講座
C言語の宣言読み方講座C言語の宣言読み方講座
C言語の宣言読み方講座
tetra_cat
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~
CHY72
 
Tickライブラリで遊ぼう(C++)
Tickライブラリで遊ぼう(C++)Tickライブラリで遊ぼう(C++)
Tickライブラリで遊ぼう(C++)
kotoji
 
Unity + C#講座①
Unity + C#講座①Unity + C#講座①
Unity + C#講座①
Yu Yu
 
C++でぼくが忘れがちなこと
C++でぼくが忘れがちなことC++でぼくが忘れがちなこと
C++でぼくが忘れがちなこと
Toshihiko Ando
 

Similar to C++ ポインタ ブートキャンプ (20)

Python と型ヒント (Type Hints)
Python と型ヒント (Type Hints)Python と型ヒント (Type Hints)
Python と型ヒント (Type Hints)
 
boost::shared_ptr tutorial
boost::shared_ptr tutorialboost::shared_ptr tutorial
boost::shared_ptr tutorial
 
リテラルと型の話 #__swift__
リテラルと型の話 #__swift__リテラルと型の話 #__swift__
リテラルと型の話 #__swift__
 
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
 
Effective Modern C++ Item 7&8
Effective Modern C++ Item 7&8Effective Modern C++ Item 7&8
Effective Modern C++ Item 7&8
 
Wrapping a C++ library with Cython
Wrapping a C++ library with CythonWrapping a C++ library with Cython
Wrapping a C++ library with Cython
 
F#によるFunctional Programming入門
F#によるFunctional Programming入門F#によるFunctional Programming入門
F#によるFunctional Programming入門
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
 
Python 学習教材 (~299ページ)
Python 学習教材 (~299ページ)Python 学習教材 (~299ページ)
Python 学習教材 (~299ページ)
 
C言語の宣言読み方講座
C言語の宣言読み方講座C言語の宣言読み方講座
C言語の宣言読み方講座
 
Inside winnyp
Inside winnypInside winnyp
Inside winnyp
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~
 
研究生のためのC++ no.2
研究生のためのC++ no.2研究生のためのC++ no.2
研究生のためのC++ no.2
 
Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
About Pointer
About PointerAbout Pointer
About Pointer
 
解説#1 C言語ポインタとアドレス
解説#1 C言語ポインタとアドレス解説#1 C言語ポインタとアドレス
解説#1 C言語ポインタとアドレス
 
Tickライブラリで遊ぼう(C++)
Tickライブラリで遊ぼう(C++)Tickライブラリで遊ぼう(C++)
Tickライブラリで遊ぼう(C++)
 
Unity + C#講座①
Unity + C#講座①Unity + C#講座①
Unity + C#講座①
 
C++でぼくが忘れがちなこと
C++でぼくが忘れがちなことC++でぼくが忘れがちなこと
C++でぼくが忘れがちなこと
 

More from Kohsuke Yuasa

More from Kohsuke Yuasa (11)

オーディオ用レベルメータを作ってみよう
オーディオ用レベルメータを作ってみようオーディオ用レベルメータを作ってみよう
オーディオ用レベルメータを作ってみよう
 
Juceで作るオーディオアプリケーション
Juceで作るオーディオアプリケーションJuceで作るオーディオアプリケーション
Juceで作るオーディオアプリケーション
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術
 
最近のC++ @ Sapporo.cpp #5
最近のC++ @ Sapporo.cpp #5最近のC++ @ Sapporo.cpp #5
最近のC++ @ Sapporo.cpp #5
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッド
 
Introduction to boost test
Introduction to boost testIntroduction to boost test
Introduction to boost test
 
C++ template-primer
C++ template-primerC++ template-primer
C++ template-primer
 
Read egg oven
Read egg ovenRead egg oven
Read egg oven
 
Study3 boost
Study3 boostStudy3 boost
Study3 boost
 
Sapporocpp#2 exception-primer
Sapporocpp#2 exception-primerSapporocpp#2 exception-primer
Sapporocpp#2 exception-primer
 

Recently uploaded

Recently uploaded (7)

新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 

C++ ポインタ ブートキャンプ