ポインタの操作
 
(このコンテンツはメールマガジンの STL & iostream 入門に手を加えたものです。「 STL と iostream が使えるかのチェック」等はメールマガジンの方のページをご覧ください)
 
 ポインタの移動 ( #05 )
 
  std::strstream を使ってデータを書き込む場合、「どんどん後ろへと追加していく」ことになります。たとえば、「一度書き込みを中断して再び書き込み」をしても、一番最後にそのまま追加します。
 
////////////////////////////////////////////////////////////////
//  書いて追加。

#include <stdio.h>
#include <iostream>
#include <strstream>

void WriteAndAppend()
{
    char ch[130];
    std::strstream cStrStrm( ch, 128, std::ios::out );

    cStrStrm
        << "ABCD";    // 一休み。
    cStrStrm
        << "EFG"      // 再び書き込み。
        << std::ends; // 終端文字も。
    cStrStrm
        << "HIJ"      // さらに書き込み。
        << std::ends;
    printf( "%s\n", ch );
}

// 結果
ABCDEFG

/////////////////////////////
	
 
文字配列の中身  まず "ABCD" と書き込みます。ここで std::ends は渡していないので、「終端文字( '\0' )」が書き込まれていないことに注意してください。この時点ではまだ文字列は閉じていません。
 次に、再び "EFG" と書き込みます。結果のように、この文字列はそのまま "ABCD" の後ろに継なげられています。この最後には「終端文字」を書き込んでおきます。
 最後に "HIJ" と書き込みます。が、これは出力結果に現れていません。これは直前の「終端文字」の後ろへと文字を追加しているからです。実際に文字は追加されているのですが、表示するときには「終端文字」までなので、その後ろの "HIJ" が表示されないことになります。
 以上の結果から「 std::strstream の文字追加は、終端文字を目安にしているわけではない」ということが分かると思います。
 
 実は std::strstream は、内部に「どこに書き込むか」を示すポインタを持っています。文字列を書き込むとそのポインタを進めて、次の書き込みを待ちます。再び書き込みが行われれば、そのポインタの位置から書き込みを始めます。
 このポインタの位置は、 std::strstream の std::strstream::seekp() というメンバ関数を使うことで移動することができます。
 
////////////////////////////////////////////////////////////////
//  ポインタを移動します。

#include <stdio.h>
#include <iostream>
#include <strstream>

void MovePointer()
{
    char ch[130];
    std::strstream cStrStrm( ch, 128, std::ios::out );

    cStrStrm
        << "ABCDEFG"
        << std::ends;
    printf( "%s\n", ch );

    cStrStrm.seekp( 2 );  // 先頭から3文字目へ移動。
    cStrStrm
        << "345";         // std::ends は付けません。
    printf( "%s", ch );
}

// 結果
ABCDEFG
AB345FG

/////////////////////////////
	
 
seekp(2) 後の文字配列  まず "ABCDEFG" と文字列を書き込みます。終端文字も追加します。
 そのあと std::strstream::seekp() というメンバ関数を呼び出します。引数には「先頭から何文字目か」を示す整数値を渡します(先頭は「 0 」です)。上の例では「 2 」なので、先頭から3文字目、「 C 」の文字の位置に移動したことになります。
 ふたたび文字列をコピーします。こんどコピーした "345" は、3文字目から上書きされます。その結果、 "CDE" の文字が "345" に変わっています。また、終端文字は加えていないので、その後ろの "FG" はそのまま残っていて、ちゃんと表示されます。
 「 std::strstream 型変数( ここでは cStrStrm )」の中には「どこから書き込むか」ということを示すポインタが入っていて、std::strstream::seekp() を使うことでそのポインタを移動できるというわけです。
 
 ポインタの位置 ( #06 )
 
 前回使用した std::strstream::seekp() は「相対位置」で移動することもできます。「一番最初から」「一番最後から」「今いる位置から」移動することができます。
 
////////////////////////////////////////////////////////////////
//  ポインタを相対位置で移動します。

#include <stdio.h>
#include <iostream>
#include <strstream>

void MovePointerRelativity()
{
    char ch[130];
    std::strstream cStrStrm( ch, 128, std::ios::out );

    cStrStrm
        << "ABCDEFG"
        << std::endl << std::ends;
    printf( "%s", ch );

    cStrStrm.seekp( 1, std::ios::beg );  // 先頭から2文字先へ移動。
    cStrStrm
        << '2';
    cStrStrm.seekp( -( 128 - 7 + 2 ), std::ios::end ); // 後ろから2。
    cStrStrm
        << '6';
    cStrStrm.seekp( -3, std::ios::cur );  // そこから2文字前へ。
    cStrStrm
        << '4';
    printf( "%s", ch );
}

// 結果
ABCDEFG
A2C4E6G

/////////////////////////////
	
 
 「一番最初から」を示すフラグは std::ios::beg です。「 std::ios:: 」はおなじみですね、前に出てきた std::ios::right などと同じです。このフラグを第2引数に渡すと、第1引数の整数が「先頭からの位置」という意味になります。これは「第2引数を省略した std::strstream::seekp() 」と同じです。
 「一番最後から」を示すフラグは std::ios::end です。ここでの「一番最後」は「 cStrStrm を作ったときに第2引数に渡した値」だということに注意してください。「現在のポインタ」とか「終端文字のある場所」とかと間違えてしまわないでください。また、このように移動距離はマイナスも使えます。
[注:ライブラリの中には std::ios::end が「終端文字のある場所」から移 動するものもありました。ご注意ください]
 「今いる位置から」を示すフラグは std::ios::cur です。このときは"ABCDEFG" の "G" にポインタがあるので、そこからの3つ前に移動しています。現在位置からの相対移動をするときには、このフラグを使います。
 
透明
透明
■ C++ : 関数のオーバーロード ( #07 )
  C++ には「関数のオーバーロード」という機能があります。これは「同名の引数の違う関数を作れる」というものです。
 例えば、次のふたつの関数のように。
 

void OverloadFunc()
{
    printf( "()\n" );
}

void OverloadFunc( int p_i )
{
    printf( "( %d )\n", p_i );
}
	
 
 このふたつの関数は同じ名前ですが、引数が違います。これを「関数 OverloadFunc() はオーバーロードされている」と表現します。
 このふたつの関数は、呼び出すときの引数の指定によって自動的に選ばれます。
 
////////////////////////////////////////////////////////////////
//    OverloadFunc() の使用例。

void Use_OverloadFunc()
{
    OverloadFunc();
    OverloadFunc( 100 );
}

// 結果
()
( 100 )

/////////////////////////////
	
 
 引数なしで呼ぶか、整数値を渡して呼ぶかで決定されるというわけです。
 std::strstream::operator<<() や std::strstream::seekp() が、渡す値の型や数で呼ばれるメンバ関数が変わるのは、この機能のおかげなのです。
 ただし、オーバーロード関数のうちどれが呼ばれるかは、コンパイル時に暗黙的に行われるため、どの関数が呼ばれるか実行してみないと分かりません。そのため、思った通りの結果をもたらさない場合もあるため、多用は禁物と言えます。
 
 「演算子のオーバーロード」は、基本的には同じ意味です。組込型に対する演算子には「デフォルトの関数」なるものがあって、その関数に対してオーバーロードする、と考えればいいかもしれません。
透明
透明
 
 
 「あれ、なんで "G" にポインタがあるの?」と思うかもしれません。書き込みポインタは常に「次に書き込む位置」を指すようになっています。そのため、 "F" の位置に '6' を書き込んだとき、ポインタがひとつ進んでいるんです。
 これを確認するため、 std::strstream::tellp() というメンバ関数を使ってみましょう。これを使うと「今どこにポインタがあるか」が分かります。
 
////////////////////////////////////////////////////////////////
//  ポインタの位置を取得して、移動します。

#include <stdio.h>
#include <iostream>
#include <strstream>

void TellPointer()
{
	char ch[130];
	std::strstream cStrStrm( ch, 128, std::ios::out );

	std::streampos lPos
		 = cStrStrm.tellp();	// 現在位置を取得。
	printf( "%d\n", (long)lPos );

	cStrStrm
		<< "ABCDEFG";

	lPos = cStrStrm.tellp();	// 現在位置を取得。
	printf( "%d\n", (long)lPos );

	cStrStrm.seekp( 3 );	// 先頭から4文字目に移動。
	lPos = cStrStrm.tellp();	// 現在位置を取得。
	printf( "%d\n", (long)lPos );
}

// 結果
0
7
3

/////////////////////////////
	
 
  std::strstream::tellp() は現在のポインタの位置を取得するメンバ関数です。この関数を呼び出すと、現在のポインタの位置が返ってきます。返ってきた値は std::streampos というクラスの変数で受け取ります。中には現在のポインタの位置が格納されています。この例で (long) としているのは、そのポインタの位置を出力するためです。
[注:ライブラリによっては std::streampos がクラスでないのものもあります。ご注意ください]
 この値から、実際のポインタの位置が分かると思います。書き込みをしてないときには、ポインタは先頭に来ています。 "ABCDEFG" を書き込むと、ポインタは先頭から8文字目、つまり "ABCDEFG" の次へと来ていることが分かります。また、 std::strstream::seekp() で移動すればもちろん同じ位置が返ってきます。
(C)KAB-studio 2000 ALL RIGHTS RESERVED.