SlideShare a Scribd company logo
1
2014/07/12
イマドキC++erの
モテカワリソース管理術!
@hotwatermorning
C++an C++am(キ++ャンキ++ャン編集部)
2
C++?メモリリークするよね?
3
メモリリーク!
4
メモリの2重解放!
5
バグが取れない
6
納期に間に合わない
7
夏を満喫できない…><
8
モテカワになれない
夏を満喫できない…><
9
モテカワになれない
イマドキC++erの
モテカワリソース管理術を使うと…!?
メモリリークしない!!
10
二重解放も発生しない!!
11
プログラムの構造もすっきり!
12
納期に間に合う!!
13
夏を満喫できる!!
14
夏を満喫できる!!
15
モテカワになる
夏を
16
満喫するための
17
リソース管理術!
18
モテカワリソース管理術解説
19
モテカワリソース管理術解説
20
✤ メモリリークはなぜ発生するのか
✤ RAIIによるリソース管理
✤ RAIIでメモリリークを制する
✤ 他の言語のリソース管理と比較
モテカワリソース管理術解説
21
✤ メモリリークはなぜ発生するのか
✤ RAIIによるリソース管理
✤ RAIIでメモリリークを制する
✤ 他の言語のリソース管理と比較
メモリリークはなぜ発生するのか
22
メモリリークはなぜ発生するのか
23
✤ そもそもメモリリークとは?
✤ 動的に確保したメモリ領域の解放し忘れ
✤ メモリリソースの無駄になる
✤ リークが増えれば、メモリリソースが枯渇する
✤ 発生すると、原因の特定が難しいバグ
メモリリークはなぜ発生するのか
✤ C言語やC++は、GC(Garbage Collection)の
仕組みを持っていない
✤ GC : プログラムの実行中に動的に確保したメモリ領域の
うち、不要になった部分を自動で解放する仕組み
24
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 malloc(	 sizeof(	 int	 )	 *	 10	 );
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;	 	 
	 	 	 	 //	 使い終わったら解放
	 	 	 	 free(	 numbers	 );
}
mallocとfreeの対応
25
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 malloc(	 sizeof(	 int	 )	 *	 10	 );
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;	 	 
	 	 	 	 //	 使い終わったのに、解放してない。
	 	 	 	 free(	 numbers	 );
}	 	 	 //	 mallocで確保した領域はもうだれも知らない
	 	 	 	 //	 =>	 メモリリーク発生
メモリリーク
26
void	 foo()
{


 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 malloc(	 sizeof(	 int	 )	 *	 10	 );
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
	 	 	 	 //	 使い終わったら解放	 	 
	 	 	 	 free(	 numbers	 );
	 	 	 	 
	 	 	 	 //	 すでに解放した領域にアクセスした
	 	 	 	 //	 =>	 Undefined	 behavior
	 	 	 	 numbers[0]	 =	 101;
}
ダングリングポインタ
27
void	 foo()
{


 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 malloc(	 sizeof(	 int	 )	 *	 10	 );
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
	 	 	 	 //	 使い終わったら解放	 	 
	 	 	 	 free(	 numbers	 );
	 	 	 	 
	 	 	 	 //	 解放した領域を再度解放しようとした
	 	 	 	 //	 =>	 Undefined	 behavior
	 	 	 	 free(	 numbers	 );
}
2重開放
28
C言語のメモリ管理
29
✤ malloc()で動的に確保したメモリ領域は、
free()で確実に一度だけ解放する必要がある
✤ ポインタ変数は、そのメモリ領域を指すだけで、
その値の有効性や管理の仕方については知らない
✤ メモリ管理は完全にプログラマに任されている
void	 readFileAndOutput()
{
	 	 	 	 const	 char*	 file_name	 =	 "input.txt";
	 	 	 	 //	 ファイルからとあるデータの配列を読み込む
	 	 	 	 FileData*	 file_data	 =	 loadFileData(	 file_name	 );
	 	 	 
	 	 	 	 for(	 int	 i	 =	 0;	 i	 <	 file_data->length_;	 i++	 )
	 	 	 	 {
	 	 	 	 	 	 	 	 outputData(	 file_data->elements_[i]	 );
	 	 	 	 }
	 	 	 
	 	 	 	 //	 使い終わったら解放する
	 	 	 	 cleanupFileData(	 file_data	 );
}
メモリ管理の複雑な例
30
//	 データを保持する構造体
typedef	 struct	 FileData_tag
{
	 	 	 	 //	 データ数
	 	 	 	 int	 length_;
	 	 	 	 
	 	 	 	 //	 データ列
	 	 	 	 DataElement	 *elements_;
}
FileData;
メモリ管理の複雑な例
31
//	 ファイルからデータの配列を読み込んで返す
FileData*	 loadFileData(	 const	 char*	 file_name	 )
{
	 	 	 	 //	 FileData型のオブジェクトを確保する
	 	 	 	 FileData*	 file_data	 =	 
	 	 	 	 	 	 	 	 malloc(	 sizeof(	 FileData	 )	 );
	  memset(	 file_data,	 0,	 sizeof(	 FileData	 )	 );
	 	 	 	 
	 	 	 	 //	 ファイルの内容をfile_dataに読み込む
	 	 	 	 parseFile(	 file_name,	 file_data	 );
	 	 	 	 //	 読み込んだデータを返す
	 	 	 	 return	 file_data;
}
メモリ管理の複雑な例
32
//	 確保したFileData型のオブジェクトを解放する
void	 cleanupFileData(	 FileData*	 file_data	 )
{
	 	 	 	 free(	 file_data	 );
}
メモリ管理の複雑な例
33
このコードには怪しい臭いが漂っている。
loadFileData関数で確保した領域を
cleanupFileDataで正しく開放しているように
見えるが・・・
明らかにそれ以外に確保している領域があるはず。
//	 ファイルからデータの配列を読み込んで返す
FileData*	 loadFileData(	 const	 char*	 file_name	 )
{
	 	 	 	 //	 FileData型のオブジェクトを確保する
	 	 	 	 FileData*	 file_data	 =	 
	 	 	 	 	 	 	 	 malloc(	 sizeof(	 FileData	 )	 );
	  memset(	 file_data,	 0,	 sizeof(	 FileData	 )	 );
	 	 	 	 
	 	 	 	 //	 ファイルの内容をfile_dataに読み込む
	 	 	 	 parseFile(	 file_name,	 file_data	 );
	 	 	 	 //	 読み込んだデータを返す
	 	 	 	 return	 file_data;
}
メモリ管理の複雑な例
34
//	 ファイルをオープンして、各行のデータを読み込む
void	 parseFile(	 const	 char*	 file_name,
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 FileData*	 file_data)
{
	 	 	 	 FILE*	 file	 =	 fopen(	 file_name	 );
	 	 	 	 fscanf(	 file,	 "%d",	 &file_data->length_	 );
	 	 	 	 //	 行数分のメモリ領域を確保
	 	 	 	 file_data->elements_	 =	 malloc(	 
	 	 	 	 	 	 	 	 sizeof(	 DataElement	 )	 *	 file_data->length_	 );
	 	 	 	 //すべての行のデータを読み込み
	 	 	 	 parseElements(	 file,
	 	 	 	 	 	 	 	 file_data->length_,	 file_data->elements_	 );
	 	 	 	 fclose(	 file	 );
}
内部で確保した領域
35
//	 ファイルをオープンして、各行のデータを読み込む
void	 parseFile(	 char*	 file_name,	 FileData*	 file_data)
{
	 	 	 	 FILE*	 file	 =	 fopen(	 file_name	 );
	 	 	 	 fscanf(	 file,	 "%d",	 &file_data->length_	 );
	 	 	 	 //	 行数分のメモリ領域を確保
	 	 	 	 file_data->elements_	 =	 malloc(	 
	 	 	 	 	 	 	 	 sizeof(	 DataElement	 )	 *	 file_data->length_	 );
	 	 	 	 //すべての行のデータを読み込み
	 	 	 	 parseElements(	 file,
	 	 	 	 	 	 	 	 file_data->length_,	 file_data->elements_	 );
	 	 	 	 fclose(	 file	 );
}
内部で確保した領域
36
//	 確保したFileData型のオブジェクトを解放する
void	 cleanupFileData(	 FileData*	 file_data	 )
{
	 	 	 	 free(	 file_data	 );
}
メモリ管理の複雑な例
37
file_dataが内部で持ってる
DataElementの配列を解放していない。
-> メモリリーク発生
//	 確保したFileData型のオブジェクトを解放する
void	 cleanupFileData(	 FileData*	 file_data	 )
{
	 	 	 	 //	 データを保持する配列が確保されていたら
	 	 	 	 if(	 file_data->elements_	 )	 {
	 	 	 	 	 	 	 	 //解放する
	 	 	 	 	 	 	 	 free(	 file_data->elements_	 );
	 	 	 	 }
	 	 	 	 free(	 file_data	 );
}
修正版
38
メモリ管理の責任
39
✤ C言語では、プログラマが明示的にメモリの確保
と解放を対応させる責任を持つ
✤ これはGC付きの言語との大きく異なる点
✤ さもなければメモリリークが発生
✤ なんらかのクリーンアップ用の関数によって確保
/解放の対応付けを集約できたとしても、結局
loadFileData()とcleanupFileData()を対応させ
るような責任がプログラマにある
public	 static	 void	 foo()
{
	 	 	 	 //	 動的に確保したメモリ領域
	 	 	 	 int[]	 numbers	 =	 new	 int[10];
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 //	 解放の処理は書かなくていい
	 	 	 	 //	 プログラム上でメモリ領域が不要になったら
	 	 	 	 //	 GCが適当なタイミングでその領域を解放する。
}
GC付き言語の例(Java)
40
GC付き言語のメモリ管理
41
✤ GC付き言語では、確保したメモリ対する解放処
理はGCによって自動的に行われるので、プログ
ラマは解放の責任を負わない
C++の場合
✤ C++にはGCが無いため、C言語と同様に、
動的に確保したメモリ領域は自動的に解放され
ない
42
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 new	 int[10];
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
	 	 	 	 //	 使い終わったらその領域を解放
	 	 	 	 delete	 []	 numbers;
}
C++の場合
43
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を確保
	 	 	 	 int*	 numbers	 =	 new	 int[10];
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
	 	 	 	 //	 解放処理を忘れると	 =>	 メモリリーク
	 	 	 	 delete	 []	 numbers;
}
	 	 	 	 //	 ダングリングポインタや2重解放もC言語と同様
C++の場合
44
C++の場合
45
✤ ただしC++には、動的にメモリ領域を扱うための
クラスが用意されている
✤ std::vector, std::list, std::deque, etc, ...
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を持つ
	 	 	 	 //	 vector<int>のオブジェクトを構築する
	 	 	 	 std::vector<int>	 numbers(	 10	 );
	 	 	 	 
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
}	 	 	 //	 vectorクラスで確保したメモリは自動で解放される
	 	 	 	 //	 ・・・どうやって?
vectorクラス
46
void	 foo()
{
	 	 	 	 //	 オブジェクト作成	 =>	 コンストラクタの呼び出し
	 	 	 	 std::vector<int>	 numbers(	 10	 );
	 	 	 	 
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
}	 	 	 //	 スコープから抜ける	 =>	 デストラクタの呼び出し
コンストラクタ/デストラクタ
47
//	 std::vectorクラスの定義のイメージ
template<class	 T>	 
class	 vector
{
private:
	 	 	 	 T	 *data_;
	 	 	 	 
public:
	 	 	 	 //	 コンストラクタ
	 	 	 	 vector(	 size_t	 capacity	 )
	 	 	 	 {
	 	 	 	 	 	 	 	 data_	 =	 new	 T[capacity];
	 	 	 	 }
クラスによるメモリ管理
48
//	 デストラクタ
	 	 	 	 ~vector()
	 	 	 	 {
	 	 	 	 	 	 	 	 delete	 []	 data_;
	 	 	 	 }
	 	 	 	 
	 	 	 	 //	 アクセッサ
	 	 	 	 T&	 operator[](	 size_t	 index	 )
	 	 	 	 {
	 	 	 	 	 	 	 	 return	 data_[index];
	 	 	 	 }
};
クラスによるメモリ管理
49
void	 foo()
{
	 	 	 	 //	 変数の定義とコンストラクタ呼び出し
	 	 	 	 std::vector<int>	 numbers;
	 	 	 	 numbers.vector(	 10	 );	 //	 指定サイズでメモリを確保
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 //	 変数の破棄時にデストラクタの呼び出し	 	 	 	 
	 	 	 	 numbers.~vector();	 //	 確保したメモリ領域を解放
}
実行イメージ(擬似コード)
50
void	 foo()
{
	 	 	 	 //	 int型10個分の領域を持つ
	 	 	 	 //	 vector<int>のオブジェクトを構築する
	 	 	 	 std::vector<int>	 numbers(	 10	 );
	 	 	 	 
	 	 	 	 numbers[0]	 =	 100;
	 	 	 	 numbers[5]	 =	 200;
	 	 	 	 numbers[9]	 =	 1000;
	 	 	 	 
}
クラスによるメモリ管理
51
クラスがメモリを管理しているのでプログラマが
メモリ管理を意識する必要がない
C++でのメモリ管理
52
✤ C++ではクラスの仕組みの中に、メモリ管理を
隠 できる
モテカワリソース管理術解説
53
✤ メモリリークはなぜ発生するのか
✤ RAIIによるリソース管理
✤ RAIIでメモリリークを制する
✤ 他の言語のリソース管理と比較
メモリ以外のリソース
54
✤ クラスの仕組みで管理できるリソースはメモリ
だけではない
✤ ファイル
✤ Mutex
✤ その他、Socket, DBコネクション, etc, ...
void	 foo()
{
	 	 	 	 //	 ファイルをオープン
	 	 	 	 std::fstream	 file(	 "output.txt"	 );	 	 	 	 
	 	 	 	 //	 ファイルに書き込み
	 	 	 	 file	 <<	 "hello	 world."	 <<	 std::endl;	 
	 	 	 	 
}	 	 	 //	 スコープを抜けるとファイルを自動でcloseする
ファイルの例
55
ファイル(ファイルハンドル)というリソースを、
std::fstreamというクラスの中で管理している
std::mutex	 g_mutex;
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
  //	 ミューテックスのロックを取得
	 	 	 	 std::lock_guard<std::mutex>	 lock(	 g_mutex	 );
	 	 	 	 	 	 	 	 
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 
}	 	 	 //	 ミューテックスのロックを解放
Mutexの例
56
ミューテックスのロック状態というリソースを、
std::lock_guardというクラスの中で管理している
std::mutex	 g_mutex;	 //	 lock()/unlock()という
          //	 メンバ関数を持つ
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
	 	 	 	 std::lock_guard<std::mutex>	 lock(	 g_mutex	 );
	 	 	 	 	 	 	 	 
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 
}
Mutexの例
57
template<class	 MutexType>
class	 lock_guard
{
private:
	  MutexType	 *m_;
public:
	 	 	 	 //	 コンストラクタ
	  lock_guard(	 MutexType&	 m	 )
	 	 	 	 :	 m_(	 &m	 )
	  {	 m_->lock();	 }
	 	 	 	 
	 	 	 	 //	 デストラクタ
	 	 	 	 ~lock_guard()
	 	 	 	 {	 m_->unlock();	 }
};
クラス定義のイメージ
58
RAII
59
✤ このように、なんらかのリソースの確保/解放を
オブジェクトの寿命に紐付けて、リソースを管理
する手法(idiom)
✤ 「あーる・えー・あい・あい」
✤ Resource Acquisition Is Initialization
✤ 三つの利点(カプセル化、例外安全性、局所性)
RAII
60
✤ このように、なんらかのリソースの確保/解放を
オブジェクトの寿命に紐付けて、リソースを管理
する手法(idiom)
✤ 「あーる・えー・あい・あい」
✤ Resource Acquisition Is Initialization
✤ 三つの利点(カプセル化、例外安全性、局所性)
std::mutex	 g_mutex;
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
	 	 	 	 std::lock_guard<std::mutex>	 lock(	 g_mutex	 );
	 	 	 	 	 	 	 	 
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 
}
Mutexの例
61
ロック状態というリソースを取得したい
=> そのためのオブジェクトを作って初期化する
RAII
62
✤ スマートポインタクラスのように、
とあるリソースの確保/解放をオブジェクトの
寿命に紐付けて、リソースを管理する方法
✤ Resource Acquisition Is Initialization
✤ 「あーる・えー・あい・あい」
✤ 三つの利点(カプセル化、例外安全性、局所性)
カプセル化
✤ さまざまなリソースを、オブジェクトの寿命とい
う形で、同じように管理できる
✤ リソース毎に異なる管理方法の詳細を知る必要が
ない
✤ ファイル : open / close
✤ ミューテックス : lock / unlock
✤ メモリ : new / delete
63
例外安全性
✤ 正常系の動作でも、例外発生時にも、同じように
リソースの解放処理が行われる
✤ 例外が発生したことによって、プログラムが不正
な状態になってしまうことがない
✤ 参考 : 「Exceptional C++―47のクイズ形式に
よるプログラム問題と解法 (C++ in-Depth
Series)」(必読)
64
std::mutex	 g_mutex;
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
	 	 	 	 //	 直接lock()/unlock()する
	  g_mutex.lock();
	 	 	 	 	 	 	 	 
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 
	 	 	 	 g_mutex.unlock();
}
例外安全ではない例
65
std::mutex	 g_mutex;
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
	 	 	 	 //	 直接lock()/unlock()する
	  g_mutex.lock();
	 	 	 	 //	 この関数が例外を投げると
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 //	 ミューテックスがロックされたままになる
	 	 	 	 g_mutex.unlock();
}
例外安全ではない例
66
std::mutex	 g_mutex;
void	 thread_proc(	 AwesomeSharedData*	 d	 )
{
	 	 	 	 std::lock_guard<std::mutex>	 lock(	 g_mutex	 );
	 	 	 	 //	 この関数が例外を投げても	 	 	 	 
	 	 	 	 ModifySharedData(	 d	 );
	 	 	 	 
}	 	 	 //	 その例外がさらに外に送出される際に変数`lock`の
	 	 	 	 //	 デストラクタが呼ばれる
例外安全になる
67
RAIIによって、
unlock()が呼ばれるのを保証できる
局所性
✤ RAIIはクラス定義にリソース確保と解放の処理を
記述する
✤ リソースを使用する側のコードがプログラム中に
散らばらない
✤ そのため、リソース管理のコードの局所性が高い
68
(余談)これからのRAII
✤ RAIIのクラスを簡単に定義するためのラッパーク
ラスが提案されている
✤ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3677.html
✤ まだ標準に取り込まれてないが、ライブラリ実装
なので、自分でも定義できる
✤ リソース管理がより簡単になる
69
モテカワリソース管理術解説
70
✤ メモリリークはなぜ発生するのか
✤ RAIIによるリソース管理
✤ RAIIでメモリリークを制する
✤ 他の言語のリソース管理と比較
スマートポインタ
71
✤ C++ではRAIIによって、クラスのオブジェクトで
メモリを管理できる
✤ さらに、C++には演算子オーバーロードによって
ポインタのように振舞うクラスが定義できる
✤ 演算子オーバーロード :
演算子の機能を再定義できる仕組み
void	 foo()
{
	 	 	 	 //	 生のポインタを受け取って中で管理するクラス
	 	 	 	 std::unique_ptr<AwesomeClass>	 p(
	 	 	 	 	 	 	 	 new	 AwesomeClass	 );
	 	 	 	 
	 	 	 	 //	 通常のポインタのように使用できる
	 	 	 	 p->MemberFunction();
	 	 	 	 p->member_variable_;
	 	 	 	 
	 	 	 	 
}
ポインタのように振舞う
72
void	 foo()
{
	 	 	 	 //	 生のポインタを受け取って中で管理するクラス
	 	 	 	 std::unique_ptr<AwesomeClass>	 p(
	 	 	 	 	 	 	 	 new	 AwesomeClass	 );
	 	 	 	 
	 	 	 	 //	 通常のポインタのように使用できる
	 	 	 	 p->MemberFunction();
	 	 	 	 p->member_variable_;
	 	 	 	 
	 	 	 	 //	 pが生きているときは
	 	 	 	 //	 内部で管理しているポインタが有効
	 	 	 	 
}	 	 	 //	 オブジェクトがデストラクトされるときに
	 	 	 	 //	 自動でdeleteを呼び出すのでリークしない
メモリを管理する
73
template<class	 T>
class	 unique_ptr
{
private:
	  T	 *p_;
	 	 	 	 
public:
	 	 	 	 //	 コンストラクタ
	  unique_ptr(	 T*	 p	 )
	 	 	 	 :	 p_(	 p	 )
	 	 	 	 {}
	 	 	 	 
	 	 	 	 unique_ptr	 (	 const	 unique_ptr&	 )	 =	 delete;
	 	 	 	 unique_ptr	 &	 
	 	 	 	 	  operator=(	 const	 unique_ptr&	 )	 =	 delete;
実装イメージ
74
//	 デストラクタ
	 	 	 	 ~unique_ptr()
	 	 	 	 {
	 	 	 	 	 	 	 	 delete	 p_;
	 	 	 	 }
	 	 	 	 
	 	 	 	 //	 アロー演算子(->)のオーバーロード
	 	 	 	 //	 unique_ptrクラスに対する->の操作を
	 	 	 	 //	 内部で管理してるポインタへの操作のように見せる
	 	 	 	 T	 *	 operator->()
	 	 	 	 {
	 	 	 	 	 	 	 	 return	 p_;
	 	 	 	 }
};
実装イメージ(続き)
75
スマートポインタを使うと
✤ deleteを意識する必要がない
✤ 例外発生時も安全
✤ メモリを返しても安全
76
void	 foo()
{
	 	 	 	 //	 生のポインタを受け取って中で管理するクラス
	 	 	 	 std::unique_ptr<AwesomeClass>	 p(
	 	 	 	 	 	 	 	 new	 AwesomeClass	 );
	 	 	 	 
	 	 	 	 //	 通常のポインタのように使用できる
	 	 	 	 p->MemberFunction();
	 	 	 	 p->member_variable_;
}
deleteを意識する必要がない
77
void	 foo()
{
	 	 	 	 A*	 p	 =	 new	 A;
	 	 	 	 
	 	 	 	 //	 この関数内で例外が発生すると
	 	 	 	 function_may_throw(	 p	 );
	 	 	 	 //	 ここまで実行パスが到達しないので
	 	 	 	 //	 メモリリーク発生
	 	 	 	 delete	 p;
}
例外発生時も安全
78
void	 foo()
{
	 	 	 	 std::unique_ptr<A>	 p(	 new	 A	 );
	 	 	 	 
	 	 	 	 //	 この関数内で例外が発生しても
	 	 	 	 function_may_throw(	 p	 );
	 	 	 	 //	 foo関数を抜ける際に変数`p`の
	 	 	 	 //	 デストラクタが安全にメモリを解放する
}
例外発生時も安全
79
void	 foo()
{
	 	 	 	 A*	 p	 =	 new	 A;
	 	 	 	 
	 	 	 	 try	 {
	  	 	 	 	 function_may_throw();
	 	 	 	 	 	 	 	 //	 正常系では正しく`p`を解放
	 	 	 	 	 	 	 	 delete	 p;
	 	 	 	 }	 catch(	 ...	 )	 {
	 	 	 	 	 	 	 	 //	 異常系でも`p`を解放する必要がある(冗長)
	 	 	 	 	 	 	 	 delete	 p;
	 	 	 	 	 	 	 	 throw;	 //再スロー(cf,	 例外中立)
	 	 	 	 }
}
try-catchでやろうとすると
80
void	 foo()
{
	 	 	 	 A*	 p	 =	 new	 A;
	 	 	 	 
	 	 	 	 try	 {
	  	 	 	 	 function_may_throw();
	 	 	 	 }	 finally	 {	 //	 擬似コード。C++にはfinallyはない
	 	 	 	 	 	 	 	 //	 正常系でも異常系でも同じく実行される
	 	 	 	 	 	 	 	 delete	 p;
	 	 	 	 }
}
finallyがあれば・・・?
81
void	 foo()
{
	 	 	 	 A*	 p	 =	 new	 A;
	 	 	 	 
	 	 	 	 try	 {
	  	 	 	 	 function_may_throw();
	 	 	 	 }	 finally	 {	 //	 擬似コード。C++にはfinallyはない
	 	 	 	 	 	 	 	 //	 正常系でも異常系でも同じく実行される
	 	 	 	 	 	 	 	 delete	 p;
	 	 	 	 }
}
finallyがあれば・・・?
82
C++にはRAIIや
後述のScopeExitがあるので
finallyは必須ではない
http://d.hatena.ne.jp/heisseswasser/20130508/1367988794
std::unique_ptr<B>	 bar()
{
	 	 	 	 std::unique_ptr<B>	 p(	 new	 B	 );
	 	 	 	 doSomethingWithB(	 p	 );


 return	 p;	 //	 関数から安全にポインタを返す
}
void	 baz()
{
	 	 	 	 std::vector<std::unique_ptr<B>>	 bs;
	  for(	 int	 i	 =	 0;	 i	 <	 10;	 ++i	 )	 {
    //	 安全な形で受け取って、取り扱う
	 	 	 	 	 	 	 	 bs.push_back(	 bar()	 );
	 	 	 	 }
}
ポインタを安全に返せる
83
make_uniqueを使おう
84
✤ make_uniqueとよばれるファクトリ関数を用意
すると、newも意識する必要がなくなる
✤ これは、C++11の標準には入っていない。
C+14から標準入りする予定。ただしC++11環境でも自作
するのは難しくない
✤ http://stackoverflow.com/questions/7038357/make-unique-and-perfect-forwarding
✤ http://herbsutter.com/gotw/_102/
//	 動的に作成したオブジェクトを直接
//	 unique_ptrの形で構築するラッパー関数があれば
template<class	 T>
std::unique_ptr<T>	 make_unique()
{
	  return	 std::unique_ptr<T>(	 new	 T	 );
}
//	 使う側でnewを意識する必要もなくなる
void	 foo()
{
	 	 	 	 std::unique_ptr<AwesomeClass>	 p	 =
	 	 	 	 	 	 	 	 make_unique<AwesomeClass>();
	 	 	 	 	 	 	 	 
}
make_uniqueを使おう
85
//	 動的に作成したオブジェクトを直接
//	 unique_ptrの形で構築するラッパー関数があれば
template<class	 T>
std::unique_ptr<T>	 make_unique()
{
	  return	 std::unique_ptr<T>(	 new	 T	 );
}
//	 使う側でnewを意識する必要もなくなる
void	 foo()
{
	 	 	 	 std::unique_ptr<AwesomeClass>	 p	 =
	 	 	 	 	 	 	 	 make_unique<AwesomeClass>();
	 	 	 	 	 	 	 	 
}
make_uniqueを使おう
86
プログラマがnewもdeleteも意識する必要が
なくなった
make_uniqueを使おう
87
✤ make_uniqueのような、スマートポインタの
ファクトリ関数を使用するのには正当な理由があ
る
✤ newを使っていると、微妙なケースでメモリリークの原因
となることがある
✤ autoキーワードと組み合わせれば、type量も減らせる
void	 foo(	 std::unique_ptr<A>	 pa,
	 	 	 	 	 	 	 	 	 	 std::unique_ptr<B>	 pb	 )
{
	 	 	 	 //...
}
void	 bar()
{
	 	 	 	 //	 foo関数呼び出し時の実引数の評価順は不定
	  foo(	 std::unique_ptr<A>(new	 A),
	 	 	 	 	 	 	 	 	 std::unique_ptr<B>(new	 B)	 )
}
newを使うと危険な場合
88
void	 foo(	 std::unique_ptr<A>	 pa,
	 	 	 	 	 	 	 	 	 	 std::unique_ptr<B>	 pb	 )
{
	 	 	 	 //...
}
void	 bar()
{


 //new	 Aが評価された直後、unique_ptr<A>の
	 	 	 	 //コンストラクタが評価されるより前に
	 	 	 	 //new	 Bが評価されるかもしれない
	  foo(	 std::unique_ptr<A>(new	 A),
	 	 	 	 	 	 	 	 	 std::unique_ptr<B>(new	 B)	 )
}
newを使うと危険な場合
89
void	 foo(	 std::unique_ptr<A>	 pa,
	 	 	 	 	 	 	 	 	 	 std::unique_ptr<B>	 pb	 )
{
	 	 	 	 //...
}
void	 bar()
{
	 	 	 	 //	 その場合、new	 Bが例外を投げたら?
	 	 	 	 //	 =>	 Aがリークする
	  foo(	 std::unique_ptr<A>(new	 A),
	 	 	 	 	 	 	 	 	 std::unique_ptr<B>(new	 B)	 )
}
newを使うと危険な場合
90
//	 動的に作成したオブジェクトを直接
//	 unique_ptrの形で構築するラッパー関数があれば
template<class	 T>
std::unique_ptr<T>	 make_unique()
{
	  return	 std::unique_ptr<T>(	 new	 T	 );
}
//	 make_uniqueの返り値の型はunique_ptrだと
//	 分かっているのでC++11の型推論キーワードが使える
void	 foo()
{
	 	 	 	 auto	 p	 =	 make_unique<AwesomeClass>();
	 	 	 	 	 	 	 	 
}
autoキーワードと組み合わせる
91
void	 readFileAndOutput()
{
	 	 	 	 const	 char*	 file_name	 =	 "input.txt";
	 	 	 	 //	 ファイルからとあるデータの配列を読み込む
	 	 	 	 std::unique_ptr<FileData>	 file_data	 =
	 	 	 	 	 	 	 	 loadFileData(	 file_name	 );
	 	 	 	 for(	 int	 i	 =	 0;	 i	 <	 file_data->elements_;	 i++	 )
	 	 	 	 {
	 	 	 	 	 	 	 	 outputData(	 file_data->[i]	 );
	 	 	 	 }
	 	 	 	 //	 使い終わったら・・・
	 	 	 	 //	 =>	 何もする必要がない!
}
最初の例がどうなるか
92
//	 データを保持する構造体
struct	 FileData
{
	 	 	 	 FileData()	 :	 num_data_(0)	 {}
	 	 	 	 //	 データ数
	 	 	 	 int	 num_data_;
	 	 	 	 //	 データ列
	 	 	 	 std::unique_ptr<DataElement[]>	 elements_;
};
最初の例がどうなるか
93
//	 ファイルからデータの配列を読み込んで返す
std::unique_ptr<FileData>	 
	 	 	 	 loadFileData(	 const	 char*	 file_name	 )
{
	 	 	 	 //	 FileData型のオブジェクトを確保する
	 	 	 	 auto	 file_data	 =	 std::make_unique<FileData>();
	 	 	 	 //	 ファイルの内容をfile_dataに読み込む
	 	 	 	 parseFile(	 file_name,	 file_data.get()	 );
	 	 	 	 //	 読み込んだデータを返す
	 	 	 	 return	 file_data;
}
最初の例がどうなるか
94
struct	 FCloser	 //	 デストラクト時に呼ばれる処理
{
	 	 	 	 void	 operator()(	 FILE*	 file	 )	 {	 fclose(file);	 }
};
//	 ファイルをオープンして、各行のデータを読み込む
void	 parseFile(const	 char*	 file_name,	 
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 FileData*file_data)
{
	 	 	 	 std::unique_ptr<FILE,	 FCloser>	 file(
	 	 	 	 	 	 	 	 	 	 	 	 fopen(	 file_name,	 "r"	 )	 );
	 	 	 	 fscanf(	 file.get(),	 "%d",	 &file_data->length_	 );
最初の例がどうなるか
95
//	 行数分のメモリ領域を確保
	 	 	 	 file_data->elements_	 =
	 	 	 	 	 	 	 	 std::make_unique<DataElement[]>(
	 	 	 	 	 	 	 	 file_data->length_	 );
	 	 	 	 //	 すべての行のデータを読み込み
	 	 	 	 parseElements(	 file.get(),
	 	 	 	 	 	 	 	 file_data->length_,
	 	 	 	 	 	 	 	 file_data->elements_.get()	 );
}
//	 クリーンアップ用の関数はいらなくなる
最初の例がどうなるか
96
RAIIでメモリリークを制する
97
✤ スマートポインタはメモリ管理にRAIIという手法
を使用したクラス
✤ スマートポインタを使用すると、メモリリークが
発生しないプログラムが書ける
✤ スライドでは紹介していないが、2重解放やダングリング
ポインタも発生しなくなる
モテカワリソース管理術解説
98
✤ メモリリークはなぜ発生するのか
✤ RAIIによるリソース管理
✤ RAIIでメモリリークを制する
✤ 他の言語のリソース管理と比較
他の言語のリソース管理
✤ 専用のスコープを用意する
✤ スコープを抜ける時に処理をする
99
他の言語のリソース管理
✤ 専用のスコープを用意する
✤ スコープを抜ける時に処理をする
100
専用のスコープを用意する
✤ C# : using構文
✤ Java : try-with-resource文
✤ Python : withステートメント
✤ Ruby : ブロック
101
public	 static	 void	 Function	 (	 string	 strFilename	 )
{
	 	 	 	 using	 (	 StreamWriter	 stream	 =
	 	 	 	 	 	 	 	 	 	 	 	 	 new	 StreamWriter	 (	 strFilename	 )	 )
	 	 	 	 {
	 	 	 	 	 	 	 	 stream.WriteLine("Loan	 Pattern!");
	 	 	 	 }
}
C#
102
File::open(	 "output.txt",	 "w"	 )	 {	 |f|
	 	 	 	 #	 オープンされたファイルオブジェクトを
	 	 	 	 #	 使ってごにょごにょ
	 	 	 	 f.puts	 "Block!"
}
#	 ブロックを抜けると自動でクローズされる
Ruby
103
Loan Pattern
104
✤ これらの言語のリソース管理の仕組みは、
ある関数や文のスコープ内でリソースを確保する
ようになっている
✤ C++のような、リソース管理がクラスのオブジェクトに
紐づく仕組みとは異なる点
✤ ScalaではLoan Patternと呼ばれる
✤ とあるスコープの中でだけリソースを拝借する
✤ Loan Patternも先に挙げたRAIIの3つの利点
(カプセル化、例外安全性、局所性)を持つ
RAII vs. Loan Pattern
✤ RAIIはリソースの管理に専用の構文が必要ない
✤ RAIIではリソースがオブジェクトに紐づいている
✤ リソースを持ち運べる
✤ スコープを超えてリソースを保持できる
✤ Loan Patternでは実現できないRAIIの利点
105
//	 なにか条件によって初期化状態が
//	 ことなるリソースを作る関数
MyResource
	  foo(	 Condition	 cond,	 Data	 data	 )
{
	  MyResource	 res(
	 	 	 	 	  getResourceHandle()	 );
	 	 	 	 if(	 cond	 )	 {
	 	 	 	 	 	 	 	 res.setStateWithData(data);
	  else	 {
	 	 	 	 	 	 	 	 res.setAnotherState();
	 	 	 	 }
	 	 	 	 return	 res;
}
リソースの受け渡し
106
C++でLoan Pattern
✤ Loan PatternはC++でも実装可能
✤ http://dev.activebasic.com/egtra/2014/03/24/654/
✤ http://carefulcode.blogspot.jp/2013/05/rifl-vs-raii.html
107
他の言語のリソース管理
✤ 専用のスコープを用意する
✤ スコープを抜ける時に処理をする
108
スコープを抜ける時に処理をする
109
✤ ScopeExitと呼ばれたりする
✤ D言語
✤ scope(exit), scope(failure)など
✤ RAIIも使える
✤ Go言語
✤ defer句(関数スコープのみ)
void	 abc()
{
	 	 	 	 Mutex	 m	 =	 new	 Mutex;
	 	 	 	 lock(m);

 //	 mutexをロック
	 	 	 	 //	 スコープ終了時にアンロック
	 	 	 	 scope(exit)	 unlock(m);
	 	 	 	 foo();

 //	 処理を行う
}
scope(exit) (D言語)
110
参考: http://www.kmonos.net/alang/d/exception-safe.html
RAIIのようなリソース管理用の専用の
クラスを作る必要がない => 簡単
void	 abc()
{
	 	 	 	 Mutex	 m	 =	 new	 Mutex;
	 	 	 	 lock(m);

 //	 mutexをロック
	 	 	 	 //	 スコープ終了時にアンロック
	 	 	 	 scope(exit)	 unlock(m);
	 	 	 	 foo();

 //	 処理を行う
}
scope(exit) (D言語)
111
参考: http://www.kmonos.net/alang/d/exception-safe.html
エラーが発生したときにだけ呼び出される
scope(failure)もあったり
C++でもScope Exit
112
✤ Boost.ScopeExitのように、C++でもScopeExit
を実現可能
✤ ただし言語機能ではなくライブラリ実装なので、D言語よ
りは冗長さがある
✤ https://sites.google.com/site/boostjp/tips/scope_guard
✤ www.boost.org/doc/libs/release/libs/scope_exit/
まとめ
113
✤ C++ではクラスの仕組みの中に、メモリ管理を
隠 できる
✤ クラスの仕組みの中でリソースを管理する手法を
RAIIと呼ぶ
✤ RAIIを使ったスマートポインタというクラスで
メモリリークを回避できる
参考文献やサイトなど
✤ Effective C++ 第3版
✤ Exceptional C++
✤ C++ ポケットリファレンス
✤ Working Draft, Standard for Programming Language C++ N3337
✤ http://herbsutter.com/gotw/_102/
✤ https://sites.google.com/site/boostjp/tips/scope_guard
✤ www.boost.org/doc/libs/release/libs/scope_exit/
✤ http://d.hatena.ne.jp/heisseswasser/20130508/1367988794
✤ http://dev.activebasic.com/egtra/2014/03/24/654/
✤ http://www.ne.jp/asahi/hishidama/home/tech/scala/sample/using.html
✤ http://carefulcode.blogspot.jp/2013/05/rifl-vs-raii.html
✤ http://www.kmonos.net/alang/d/exception-safe.html
114

More Related Content

PDF
C++ マルチスレッドプログラミング
PDF
C++ マルチスレッド 入門
PDF
組み込み関数(intrinsic)によるSIMD入門
PDF
規格書で読むC++11のスレッド
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PPTX
非同期処理の基礎
PDF
ゲーム開発者のための C++11/C++14
PDF
templateとautoの型推論
C++ マルチスレッドプログラミング
C++ マルチスレッド 入門
組み込み関数(intrinsic)によるSIMD入門
規格書で読むC++11のスレッド
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
非同期処理の基礎
ゲーム開発者のための C++11/C++14
templateとautoの型推論

What's hot (20)

PDF
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
PDF
組み込みでこそC++を使う10の理由
PDF
LLVM最適化のこつ
PDF
闇魔術を触ってみた
PPTX
BoostAsioで可読性を求めるのは間違っているだろうか
PPTX
競技プログラミングのためのC++入門
PDF
unique_ptrにポインタ以外のものを持たせるとき
PDF
LLVM Backend の紹介
PDF
20分くらいでわかった気分になれるC++20コルーチン
PDF
いまさら聞けない!CUDA高速化入門
PDF
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
PDF
プログラムを高速化する話
PDF
すごい constexpr たのしくレイトレ!
PDF
目grep入門 +解説
PDF
C++ ポインタ ブートキャンプ
PDF
マルチコアを用いた画像処理
PDF
中3女子が狂える本当に気持ちのいい constexpr
PDF
不遇の標準ライブラリ - valarray
PDF
Constexprとtemplateでコンパイル時にfizz buzz
PDF
Rustに触れて私のPythonはどう変わったか
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
組み込みでこそC++を使う10の理由
LLVM最適化のこつ
闇魔術を触ってみた
BoostAsioで可読性を求めるのは間違っているだろうか
競技プログラミングのためのC++入門
unique_ptrにポインタ以外のものを持たせるとき
LLVM Backend の紹介
20分くらいでわかった気分になれるC++20コルーチン
いまさら聞けない!CUDA高速化入門
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
プログラムを高速化する話
すごい constexpr たのしくレイトレ!
目grep入門 +解説
C++ ポインタ ブートキャンプ
マルチコアを用いた画像処理
中3女子が狂える本当に気持ちのいい constexpr
不遇の標準ライブラリ - valarray
Constexprとtemplateでコンパイル時にfizz buzz
Rustに触れて私のPythonはどう変わったか
Ad

Viewers also liked (20)

PPTX
C++用将棋ライブラリ "OpenShogiLib"の紹介
PDF
Boost.GraphでJR全線乗り尽くしプランを立てる - プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 (2014.7.12)
PDF
クロスプラットフォーム開発雑感 #pronamaclrhsapporocpp
PDF
自宅サーバーを立てる話
DOCX
Linux syllabus
PDF
JA7YCQプログラミング勉強会 第1回 ~プログラミングやってみようか~
PDF
Vi(m)を教えた話
 
PDF
コンピュータに「最長しりとり」「最短距離でのJR線全線乗り尽くし」を解いてもらった方法
PDF
C++ template-primer
PDF
Sapporocpp#2 exception-primer
PDF
2011.12.10 関数型都市忘年会 発表資料「最近書いた、関数型言語と関連する?C++プログラムの紹介」
PDF
2011.7.3 札幌C++勉強会#2「C++のマクロはどこまで関数をいじれるのか」
PDF
2012.11.17 CLR/H&札幌C++勉強会 発表資料「部分文字列の取得を 効率よく!楽に! - fundoshi.hppの紹介と今後の予定 -」
PDF
最近のC++ @ Sapporo.cpp #5
PDF
Introduction to boost test
PPT
Heliumエンジンの設計と実装
PPT
Roombaを鉄騎コントローラーで操縦してみた
PDF
こんにちは、白い粉エバンジェリストの山口です
PDF
Programming camp code reading
PDF
rsyncで差分バックアップしようぜ!
C++用将棋ライブラリ "OpenShogiLib"の紹介
Boost.GraphでJR全線乗り尽くしプランを立てる - プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 (2014.7.12)
クロスプラットフォーム開発雑感 #pronamaclrhsapporocpp
自宅サーバーを立てる話
Linux syllabus
JA7YCQプログラミング勉強会 第1回 ~プログラミングやってみようか~
Vi(m)を教えた話
 
コンピュータに「最長しりとり」「最短距離でのJR線全線乗り尽くし」を解いてもらった方法
C++ template-primer
Sapporocpp#2 exception-primer
2011.12.10 関数型都市忘年会 発表資料「最近書いた、関数型言語と関連する?C++プログラムの紹介」
2011.7.3 札幌C++勉強会#2「C++のマクロはどこまで関数をいじれるのか」
2012.11.17 CLR/H&札幌C++勉強会 発表資料「部分文字列の取得を 効率よく!楽に! - fundoshi.hppの紹介と今後の予定 -」
最近のC++ @ Sapporo.cpp #5
Introduction to boost test
Heliumエンジンの設計と実装
Roombaを鉄騎コントローラーで操縦してみた
こんにちは、白い粉エバンジェリストの山口です
Programming camp code reading
rsyncで差分バックアップしようぜ!
Ad

Similar to イマドキC++erのモテカワリソース管理術 (20)

PDF
2011.09.18 v7から始めるunix まとめ
PDF
Cpu cache arch
PDF
ソフトウェアエンジニアのための「機械学習理論」入門・ハンズオン演習ガイド
KEY
Objc lambda
PDF
initramfsについて
PDF
Altera SDK for OpenCL解体新書 : ホストとデバイスの関係
PDF
C++ lecture-0
PPT
20010901
PDF
C base design methodology with s dx and xilinx ml
PDF
C++ Transactional Memory言語拡張の紹介
PDF
Dalvikバイトコードリファレンスの読み方 改訂版
PDF
10分でわかるFuelPHP @ 2011/12
PDF
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
PPT
C language Sem 01
PDF
Javaセキュアコーディングセミナー東京第3回講義
KEY
NVIDIA Japan Seminar 2012
PDF
C++コンパイラ GCCとClangからのメッセージをお読みください
PDF
CodeIgniter東京勉強会 2011.05.14
PDF
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
PDF
RのffでGLMしてみたけど...
2011.09.18 v7から始めるunix まとめ
Cpu cache arch
ソフトウェアエンジニアのための「機械学習理論」入門・ハンズオン演習ガイド
Objc lambda
initramfsについて
Altera SDK for OpenCL解体新書 : ホストとデバイスの関係
C++ lecture-0
20010901
C base design methodology with s dx and xilinx ml
C++ Transactional Memory言語拡張の紹介
Dalvikバイトコードリファレンスの読み方 改訂版
10分でわかるFuelPHP @ 2011/12
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
C language Sem 01
Javaセキュアコーディングセミナー東京第3回講義
NVIDIA Japan Seminar 2012
C++コンパイラ GCCとClangからのメッセージをお読みください
CodeIgniter東京勉強会 2011.05.14
10分でわかるFuelPHP @ 2012/05 OSC2012 Nagoya
RのffでGLMしてみたけど...

イマドキC++erのモテカワリソース管理術