4. C++勉強会
オブジェクトによるリソースの管理。
リソースとは?
Bjarne Stroustrup (Software Development for Infrastructure)
`A resource is anything that has to be acquired and released after use.'
. . . . . .
14. C++勉強会
オブジェクトによるリソースの管理。
RAIIの応用
c l a s s Pen {
public :
HDC hdc ;
HPEN new pen ;
HGDIOBJ old pen ;
Pen (HDC hdc , Color pen color )
: hdc ( hdc ) , old pen ( NULL){
new pen = CreatePen ( . . . ) ; // リ ソ ー ス の 取 得 。
i f ( new pen )
old pen = SelectObject ( hdc , new pen ) ;
}
˜Pen ( ) {
i f ( old pen ){
SelectObject ( hdc , old pen ) ; // 元 の ブ ラ シ に 戻 す 。
DeleteObject ( new pen ) ; // リ ソ ー ス の 解 放 。
}
}
};
. . . . . .
15. C++勉強会
オブジェクトによるリソースの管理。
RAIIの応用
void draw ( ) {
// 赤 色 の ペ ン を 作 成 。 r e d p e n オ ブ ジ ェ ク ト が 生 き て い る
// ス コ ー プ で は 、 線 が 赤 色 に な る 。 r e d p e n オ ブ ジ ェ ク ト
// が 破 壊 さ れ る と ペ ン の 色 は 、 元 の 色 に 戻 る 。
Pen red pen ( hdc , RED ) ;
. . . // 描 画 。
}
. . . . . .
16. C++勉強会
オブジェクトによるリソースの管理。
RAIIを利用する際の注意点
ハンドル・クラスのコピー動作(コピー・コンスラクタと代入演算子)に注意!!
MyClassHandle my obj1 ;
MyClassHandle my obj2 ( my obj1 ) ; // コ ピ ー ・ コ ン ス ラ ク タ
// リ ソ ー ス の 使 用 。 m y o b j 1 の o b j は ど う な る ?
my obj2 . obj−>doSomething ( ) ;
// void f ( i n t x ) ; 値 渡 し 。
i n t x = 3;
f (x );
std : : cout < x < std : : endl ; // 3 と 表 示 。
< <
// void g ( MyClassHandle obj ) 値 渡 し 。
MyClassHandle my obj3 ;
g ( my obj3 ) ; // g の 呼 び 出 し 後 に m y o b j 3 の 状 態 は ?
. . . . . .
17. C++勉強会
オブジェクトによるリソースの管理。
RAIIのコピー動作
コピーを禁止
c l a s s MyClassHandle {
private :
// コ ピ ー ・ コ ン ス ラ ク タ と 代 入 演 算 子 を p r i v a t e 宣 言 。
// 実 装 は 必 要 な し 。
MyClassHandle ( const MyClassHandle & ) ;
const MyClassHandle& operator =( const MyClassHandle & ) ;
};
参照カウントを利用してリソースを共有。(shared ptrの実装)
. . . . . .
18. C++勉強会
オブジェクトによるリソースの管理。
RAIIに関連してちょこっと脱線
リソースの取得はオブジェクトの初期化で行う。 オブジェクト初期化(生成)する時点で必要な
情報が全て揃っているのが理想。 できる限り初期化関数は避けましょう。
c l a s s BadClass{
public :
void i n i t i a l i z e ( i n t x ) ; // 初 期 化 関 数 。
void doSomething ;
};
BadClass obj ; // オ ブ ジ ェ ク ト の 生 成 ( 初 期 化 )
obj . i n i t i a l i z e ( 1 ) ; // 初 期 化 関 数 に よ る オ ブ ジ ェ ク ト の " 初 期 化 "
obj . doSomething ( ) ;
BadClass obj2 ;
obj2 . doSoemthing ( ) ; // バ グ ! ! initializeの呼び出し前に他の関数
BadClass obj3 ;
obj3 . i n i t i a l i z e ( 1 ) ;
obj3 . i n i t i a l i z e ( 2 ) ; // 2回 初 期 化 関 数 を 呼 び 出 し て も 良 い ?
. . . . . .
19. C++勉強会
オブジェクトによるリソースの管理。
RAIIに関連してちょこっと脱線
初期化はコンストラクタで行う。
c l a s s BadClass{
public :
BadClass ( i n t x ) ;
void doSomething ( ) ;
};
BadClass obj ( 1 ) ;
obj . doSomething ( ) ; // 初 期 化 前 に 他 の 関 数 を 呼 び 出 せ な い 。
. . . . . .
21. C++勉強会
Interfaceの設計方法
設計指針
Make interfaces easy to use correctly and hard to use incorrectly
Effective C++
できるかぎり、コンパイラにエラーをチェックさせる。(マーフィーの法則)
C++の型システムを利用する。
. . . . . .
22. C++勉強会
Interfaceの設計方法
悪い例 その1
悪いインターフェイスの例。
パラメータの名前のみで、意味を区別しようとしている。
// 2点 間 の 距 離 を 計 算 。
i n t c a l c u l a t e D i s t a n c e ( i n t x0 , i n t y0 , i n t x1 , i n t y1 ) ;
// ユ ー ザ 認 証 。
bool aut he n tic ate ( std : : s t r i n g password , std : : s t r i n g user name ) ;
. . . . . .
23. C++勉強会
Interfaceの設計方法
悪い例 その1
// 点 ( 1 , 3 ) と 点 ( 2 , 4 ) の 距 離 を 計 算 。
i n t distance = c a l c u l a t e D i s t a n c e ( 1 , 2 , 3 , 4 ) ; // バ グ ! !
i n t distance = c a l c u l a t e D i s t a n c e ( 1 , 3 , 2 , 4 ) ; // 正 し い 。
// ユ ー ザ を 認 証 。
std : : s t r i n g user name ( " user " ) ;
std : : s t r i n g password ( " s e c r e t password " ) ;
// バ グ ! ! ユ ー ザ 名 と パ ス ワ ー ド が 逆 。
bool i s a u t h e n t i c a t e d = auth ent icate ( user name , password ) ;
// 正 し い 。
bool i s a u t h e n t i c a t e d = auth ent icate ( password , user name ) ;
使用方法を間違えても、誰も教えてくれない…
. . . . . .
24. C++勉強会
Interfaceの設計方法
改善 その1
小さなクラスを定義して利用する。
s t r u c t Point {
Point ( i n t x , i n t y )
x (x) , y (y) {
}
int x ;
int y ;
}
typedef unsinged i n t Distance ;
// イ ン タ ー フ ェ イ ス ( 引 数 の 型 や 戻 り 値 の 型 ) を 見 た だ け で
// 使 用 方 法 が 判 る 。
Distance c a l c u l a t e D i s t a n c e ( Point p1 , Point p2 ) ;
. . . . . .
25. C++勉強会
Interfaceの設計方法
改善 その2
typedef std : : s t r i n g Password ;
typedef std : : s t r i n g User ;
bool aut he n tic ate ( Password login password , User a user ) ;
User my user name ( " user " ) ;
Password my pass ( " s e c r e t password " ) ;
bool i s a u t h e n t i c a t e d = auth ent icate ( my pass , my user name ) ;
. . . . . .
26. C++勉強会
Interfaceの設計方法
改善 その2 さらなる改善
typedefではコンパイル・エラーにはならないので注意。
User my user name ( " user " ) ;
Password my pass ( " s e c r e t password " ) ;
bool i s a u t h e n t i c a t e d = auth ent icate ( my user name , my password ) ; // バ グ ! !
コンパイラーにコードの正しさをチェックさせたいときは、クラスを利用する。
c l a s s User {
...
};
c l a s s Password {
...
};
上記のコードはコンパイル・エラー。
. . . . . .
28. C++勉強会
Interfaceの設計方法
悪い例 その2
リソースの取得と解放が別々の処理に分離されている。
MyClass * create ( ) ;
void r e l e a s e ( MyClass * obj ) ;
問題点
クライアントがreleaseを呼び忘れたら?
release以外の方法で、deleteされたら?
. . . . . .
29. C++勉強会
Interfaceの設計方法
改善 その3
生のポインタではなくスマートポインタを利用する。
boost : : shared ptr<MyClass> create ( ) ;
もし、3rdパーティのライブラリで生のポインタを返すようにしている場合。
SomeonesClass * create ( ) ;
void r e l e a s e ( SomeonesClass * obj ) ;
shared ptrでは、解放の処理をカスタマイズできる。
// 第2 引 数 で 、 解 放 に 使 用 し た い 関 数 を 指 定 。
void f ( ) {
boost : : shared ptr<SomeoneClass> p t r ( create ( ) ,
std : : p t r f u n ( r e l e a s e ) ) ;
ptr−>doSomething ( ) ;
}// 自 動 的 に 解 放 。
. . . . . .
37. C++勉強会
標準ライブラリ STLの活用
使用例
typedef std : : vector<i n t> numbers ;
std : : vector<i n t> createNumbers ( ) {
std : : vector<i n t> numbers ;
f o r ( i n t i = 0; i < 10; ++ i ){
numbers . push back ( getRandomNumber ( ) ) ;
}
// 昇 順 に 並 べ 替 え 。 s o r t は デ フ ォ ル ト で (<) を 利 用 し て 並 べ 替 え る 。
std : : s o r t ( numbers . begin ( ) , numbers . end ( ) ) ;
// 降 順 に 並 べ 替 え 。 g r e a t e r 関 数 を 利 用 し て 並 べ 替 え 。
std : : s o r t ( numbers . begin ( ) , numbers . end ( ) , greater<i n t > ( ) ) ;
}
. . . . . .
38. C++勉強会
標準ライブラリ STLの活用
関数オブジェクト
operator()をメンバ関数を保持するオブジェクト。STLを拡張するために利用する。
s t r u c t MyGreater{
bool operator ( ) ( i n t x , i n t y ) {
return x > y ;
}
};
// 降 順 に 並 べ 替 え 。 M y G r e a t e r 関 数 オ ブ ジ ェ ク ト を 利 用 し て 並 べ 替 え 。
std : : s o r t ( numbers . begin ( ) , numbers . end ( ) , MyGreater ( ) ) ;
. . . . . .
39. C++勉強会
標準ライブラリ STLの活用
コーディング without STL(電話帳 氏名と電話番号を管理するプログラム)
const i n t MAX SIZE(1000);
Name *names = new Name[ MAX SIZE ] ;
PhoneNumber phone numbers = new PhoneNumber [ MAX SIZE ] ;
unsigned i n t s i z e = 0;
void addRecord (Name name , PhoneNumber phone number ){
i f ( s i z e >= MAX SIZE ){
// 最 大 長 を 越 え て い る た め メ モ リ の 確 保 と デ ー タ の コ ピ ー 。
Name * new name data = new Name[2 * s i z e ] ;
PhoneNumber * new phone number data = new PhoneNumber[2 * s i z e ] ;
f o r ( i n t i = 0; i < s i z e ; ++ i ){
new name data [ i ] = names [ i ] ;
new phone number data [ i ] = phone numbers [ i ]
}
Name *tmp1 = names ;
PhoneNumber *tmp2 = phone numbers ;
names = new name data ;
phone numbers = new phone number data ;
delete [ ] tmp1 ;
delete [ ] tmp2 ;
}
// レ コ ー ド を 追 加 。
names [ s i z e ] = name ;
phone numbers [ s i z e ] = phone number ;
++ s i z e ;
}
void sortPhoneBook (Name *names , PhoneNumber * phone numbers ){ . . . }
. . . . . .
40. C++勉強会
標準ライブラリ STLの活用
コーディング with STL
typedef std : : pair<Name, PhoneNumber> RecordType ;
typedef std : : vector<RecordType> PhoneBook ;
PhoneBook my phone book ;
void addRecord (Name name , PhoneNumber phone number ) {
my phone book . push back ( std : : make pair ( name , phone number ) ) ;
}
void sortPhoneBook ( PhoneBook& phone book ){
// M y C r i t e r i a l は 自 分 が ソ ー ト し た い 基 準 を 表 現 す る 関 数 オ ブ ジ ェ ク ト 。
std : : s o r t ( phone book . begin ( ) , phone book . end ( ) , M y C r i t e r i a ( ) ) ;
}
. . . . . .
43. C++勉強会
防衛的プログラミング
変数のスコープを最小限に
// ル ー プ 変 数 ( i ) は 、 f o r の 初 期 化 ブ ロ ッ ク で 。
f o r ( i n t i = 0; i < N; ++ i ) {
...
}
void bad ( ) {
// 関 数 の 頭 で 全 変 数 を 宣 言 。
i n t x = 0;
std : : s t r i n g s t r ( "My name i s " ) ;
...
s t r += " Takeda " ;
...
++x ;
}
void b e t t e r ( ) {
...
std : : s t r i n g s t r ( "My name i s " ) ;
s t r += " Takeda " ;
...
i n t x = 0;
++x ;
}
void best ( ) {
...
const std : : s t r i n g s t r ( "My name i s Takeda " ) ;
...
i n t x = 1; . . . . . .
44. C++勉強会
防衛的プログラミング
constの活用
できる限りconstを使用する。
c l a s s MyClass {
public :
void doSomething ( ) const ; // c o n s t メ ン バ 関 数 。
};
void f ( const MyClass& obj ) ; // パ ラ メ ー タ の c o n s t 参 照 渡 し 。
void g ( ) {
const double p i = 3.141592; // c o n s t ロ ー カ ル 変 数 。
}
. . . . . .
45. C++勉強会
防衛的プログラミング
イディオムを利用
イディオムとは?
経験のあるプログラマが利用するソース・コードの共通の書き方。
例えば、forループのイディオム。ループ条件にOpen-End(<)を利用することで、 要素数が0の
時を特殊ケース扱いとしなくてもよい(sizeが0のときは、ループは 実行されない)。
f o r ( i n t i = 0; i < s i z e ; ++ i ){
...
}
イディオムの利点:
イディオムを知っているプログラマ間で、ソースコードの理解性が向上する。
バグを発見しやすい。
. . . . . .
47. C++勉強会
参考情報
書籍
The C++ Programming Language
Effective C++
More Effective C++
Accelerated C++
The C++ Standard Library: A Tutorial and Reference
Effective STL
IEEE Computer Society January 2012 "Software Development for
Infrastructure"
. . . . . .
48. C++勉強会
参考情報
リンク
cplusplus.com
http://www.cplusplus.com/
C++ FAQ
http://www.parashift.com/c++-faq-lite/
More C++ Idioms(C++イディオム集)
http://en.wikibooks.org/wiki/More C%2B%2B Idioms
Going Native 2012 Keynote by Bjarne Stroustrup: C++11 style
http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/
Keynote-Bjarne-Stroustrup-Cpp11-Style#+LTEX CLASS
A
. . . . . .