Version 11.08
文字列クラスふたたび
『ふたたび?』
「そう。前回は Version 5.22 ( No.087 ) で」
『そういえばそのときも配列とか CString とか使ったね』
「そのときも言ったけど、文字列を配列じゃなくクラスで操作すると、いろ
んなメリットがあります」
・可変長だからサイズを気にしなくていい。
・便利な関数を使うことができる。
・演算子で操作できる。
「対してデメリットは次のようなものがあります」
・文字列を受け取るのが苦手。
・使えない環境もある。
・演算子を使うと間違った操作をすることがある。
・仕組みを知らないで使うと危険。
『デメリットの方が多いじゃん!』
「そうなんだけど」
・可変長だからサイズを気にしなくていい。
「のメリットが計り知れないんです」
『なんで?』
「それはあとのお楽しみ」
『全然楽しみじゃないし……』
「とりあえず、今回は文字列クラスについて慣れておきます。復習も兼ねて
ね」
『はーい』
「まずは普通に使ってみます」
void UseCString()
{
CString cStr;
cStr = "あいうえお";
TRACE( "%s\n", cStr );
// あいうえお
}
「今回、文字列クラスは CString を使います」
『他にもあるの?』
「うん、 std::string っていうのもあるんだけど、そっちはちょっと上級
者向けだから。で」
CString cStr;
「で、 CString の変数 cStr を作ります。この中に文字列を入れられま
す」
『入れてるのが』
cStr = "あいうえお";
『ってわけね。 = で入れられるのは便利よねー』
「それに」
TRACE( "%s\n", cStr );
「というふうに、文字列として使えるところはすべてそのまま使えます」
『便利よね』
「で、これと同じ事を char の配列で試してみます」
『便利じゃない例ってわけね』
void UseCharAry()
{
char pch[256];
strcpy( pch, "あいうえお" );
TRACE( "%s\n", pch );
// あいうえお
}
「まず、 char の配列を作ります」
char pch[256];
「当然、この配列には 256 文字以上は入りません」
『でも CString はそういうの関係なく入ります、と』
「そういうこと。それが CString のアドバンテージのひとつ。次に」
strcpy( pch, "あいうえお" );
「と、文字列を入れる時にはランタイムを使わなくちゃいけません」
『 CString なら = で簡単にできる、と』
「まぁ最後の TRACE() で出力は同じようにできるってことで」
『たったこれだけで見ると CString の方がむっちゃ便利よねー』
「じゃあ苦手な方もしておこうか。まず、普通に文字列を受け取ることがで
きません」
void UseCStringForRecieveBad()
{
CString cStr;
strcpy( cStr, "あいうえお" );
TRACE( "%s\n", cStr );
}
『あ、エラーだ』
error C2664: 'strcpy' :
1 番目の引数を 'class CString' から 'char *' に変換できません。
この変換を実行可能なユーザー定義変換演算子がないか、
または演算子を呼び出せません。
「なんでこうなるかっていうと、 strcpy() の型がこうなってるから」
char *strcpy( char *string1, const char *string2 );
「1番目の引数は char * 、2番目の引数は const char * 。で、 CString
には const char * が来た時に自動的に中の文字列を渡す機能はあるんだけ
ど、 char * に対する機能はないんです」
『なんで?』
「だって」
CString cStr;
「この時点で、中に入ってる文字列は空。配列で言えば、この中に入ってる
配列は」
char pch[0];
『げ、これじゃ文字列受け取れないね』
「そういうこと。受け取るためには」
char pch[256];
「って感じに領域を確保しなきゃいけないんです。そのためのメンバ関数が
CString::GetBuffer() と CString::ReleaseBuffer() です。というわけで
これを使って文字列を受け取ってみます」
void UseCStringForRecieve()
{
CString cStr;
strcpy( cStr.GetBuffer( 256 ), "あいうえお" );
cStr.ReleaseBuffer();
TRACE( "%s\n", cStr );
// あいうえお
}
『お、ちょっと長いけど受け取れてる』
「 CString::GetBuffer() は、引数で渡されたサイズ分、文字列を置く領域
を確保します」
『つまり』
char pch[256];
『ってするってことね』
「そういうこと。で、この戻り値の型は char * だから、これをそのまま渡
す事ができます」
『で、受け取れるわけね。そのあとの CString::ReleaseBuffer() ってなに
やってんの?』
「これは後始末。たとえば文字列のサイズの取り直しとか」
『文字列のサイズ?』
「 CString の中に持っている〈文字列サイズ〉を取得し直してるんです。
中に入ってる文字列が変わったわけだから、そういう情報も取り直さないと
いけないから」
『なるほどねー』
「で、これをもう一度見て欲しいんだけど」
strcpy( cStr.GetBuffer( 256 ), "あいうえお" );
『文字列を受け取るために、中身を広げてるとこだね』
「でも、これって無駄なんです。サイズはわかってるんだから」
void UseCStringForRecieveOptimizeSize()
{
CString cStr;
strcpy
( cStr.GetBuffer( strlen( "あいうえお" ) + 1 )
, "あいうえお"
);
cStr.ReleaseBuffer();
TRACE( "%s\n", cStr );
// あいうえお
}
『 strlen() で文字列のサイズを取得……して、それ使って中身広げれば絶
対に受け取れるサイズになるってわけね。あれ? でもさ、同じ事を配列で
もすればいいんじゃないの?』
「ところが、これは配列では使えないんです」
void UseCharAryBad()
{
char pch[ strlen( "あいうえお" ) ];
strcpy( pch, "あいうえお" );
TRACE( "%s\n", pch );
}
『あ、エラーだ』
error C2057: 定数式が必要です。
「配列のサイズは、コンパイルするときに判っていないといけないんです。
だから、プログラムの途中でサイズを変えたくても変えられないんです」
『でも CString はできてたよね』
「そう、もちろんその方法はあって、でもそれが面倒なんです。というわけ
で次回に続く!」
/*
Preview Next Story!
*/
『 CString ってやっぱり便利ねー』
「でもそのためには仕組みを知らなくちゃ」
『知らなくちゃいけないの?』
「下手な使い方するとまずいことになるから」
『プログラミングってそういうの多いよね』
「でも、多い割には知られてない事も結構……」
『げ』
「というわけで次回」
< Version 11.09 メモリを動的に確保する >
『につづく!』
「というわけでやっぱり仕組みを勉強してもらいます」
『やっぱり面倒そう……』