C++ streamクラスについて 書いて覚えるための初心者自己中記事
今まで使用していた iostream やら fstream やらはクラスの継承によって派生しているものらしい。
stream とは入出力に関してのクラスで、基底クラスでは
istream <-inpot用
ostream <-output用
が存在する。
そこからの派生クラスとして
iostream <- inpou & output用
があるらしい。
二つのクラスを継承するとかもあるんだなぁ。
さらにそのiostreamから派生したのが
fstream <- ファイル関係
stringstream <- 文字列関係
なんだとか。
ちなみにそれとは別に大元の
isreamからiftream / istrungstream
ostreamからofstream / ostringstream
があるけど
iostreamから派生したfstream / stringstream とは継承関係はない。
ややこしい。
[ istream ] [ ostream ]
↑ ↑ ↑ ↑ ↑ ↑
[ ifstream ] [ istringstream ] [ iostream ] [ ofstream ] [ ostringstream ]
↑ ↑
[ fstream ] [ stringstream ]
こんな感じ?
アップキャスト可能か不可能かをこれで判断するらしい。
ostream& を引数に取る関数を用意した場合、
ostream
iostream
ofstream
ostringstream
fstream
stringstream
からのオブジェクトを渡せるということ。
なんか超絶めんどくさそうな予感・・・。
用語
データを一旦貯めておく領域を バッファ( buffer )
データを一旦貯めておく行為を バッファリング ( buffering )
バッファリングしたデータをファイルに出力する フラッシュ ( flush )
ostreamクラスのオブジェクトで
標準出力
のオブジェクトがある。
標準出力には cout
標準エラー出力には cerr / clog
標準出力も標準エラー出力もデフォルトでは画面への出力。同じ。
しかし、これらは出力先を変更することができる。 ( redirect )
これまで使ってきた、cout , cin , fstream , stringstream , read , write , getline , << , >>
, fail , eof , clear などstream に関わっていたものは継承で共通化されている。
そのため、cin で間違った入力をしてしまったりすると fail がtrue 状態。
clear するまで入力無効!!
とかなるらしい。
~~~~~~~~~~
数時間経過
~~~~~~~~~~
windowsノートが届いたからxcodeからvisual studioにチェンジしてみようと思ったら凄い大変。(使うのほぼ初めて)
xcodeが分かりやす過ぎてvisual studioがわかりにく過ぎる・・・。
visual studio 2017 communityをインストールするときに何を入れますか?的なのがもうしんどい。
C++の勉強がしたいんですけど、逆になにをいれたら出来るんですか?って感じ。
まじ素人にやさしくない。
んで何かいろいろ検索したら win32コンソールアプリケーションなるものが目的のものらしいと分かったけど、新規プロジェクトでそれがない!!
検索しても誰かのスクショでちゃんと有る!!
意味が分からない。
何回かアンインストールして再度インストールしてみても意味なし。
仕方ないので「空のなんちゃら」ってやつを選択したらそれっぽい感じで、もうこれでいいんじゃね?ってかんじ。
これからはvisual studioでがんばるぞい!!
再開
bool SkipOnError(istream& istr) { if (istr.fail()){ if (istr.eof()) { exit(EXIT_FAILURE); } char ch; //ここの変数が istr.clear(); istr >> ch; //ここで何してるのかわからない return true; } else { return false; } } void main() { int n; do { cin >> n; } while (SkipOnError(cin)); // cin を引数にしてるのもわからない cout << "入力された値は" << n << "です。" << endl; system("pause"); }
うわぁ・・・・
xcodeはただのコピペで綺麗に貼れたのに。
仕方がない。徐々によくなっていくだろう。
というか慣れたらこっちのが良いかもね。
今回は int 型変数 n に対して cin で入力を行って、型に入らない入力の時はやり直させる感じです。
コードに赤字で書いたのが理解できない点。
cin を引数にするってなに?
そういえば前にチラッと cin は istreamクラスのオブジェクトってテキストに書いてあるのみたな。
オブジェクトっていうとあれか? istream cin; みたいな感じなのか?
cin >> n; で入力された値を n に入れてるわけだけど cin にも入ってるって事かな?
ちょっとこの辺の説明がなくて分からない。
cin を引数にするのは漠然と飲み込めるけど、SkipOnError関数内で行われているchar型変数の動きが全く分からない。
一旦、
char ch;
istr >> ch;
をコメントアウトしてみたら数値以外を入力したときに次の入力が出来ない状態になった。これはヒントになる・・。
だめだ。わからない。
とりあえず保留だぁ。。。
で、ストリームの流れを操作することが出来るものがあって、これをマニピュレーター(manipulator)という。
テキストままでいくと、
cout << マニピュレーター;
cin >> マニピュレーター;
と使う。
print("%02X" , buf [ w ] );
とやっていたことがマニピュレーターを使って出来る。
cout << setw(2) << setfill( '0' ) << hex << uppercase << (int) buf [ w ] << ' ' ;
らしい。
setw (2) は最小幅
setfill ( '0' ) は最小幅に達しない場合に埋めるものの指定
hex は16進数を指定するという意味
uppercase は大文字でという意味
これらはcout に与えられた効果みたいな感じ。バフかな?
ただし効果時間がほとんど永続。
setw (2) のみが一度きり。他は永続。
なのでループの中で使いたい場合はsetw 以外はループの外にしたほうがいい。
この永続効果のマニピュレーター、cout を他の使い方で使用したい場合はどうするのか。
マニピュレーター使用する前の設定を保存しておいて、使用終了したら保存していた設定を使って元に戻す。
void main() { ios::fmtflags flags = cout.flags(); //設定保存 char fill = cout.fill(); //設定保存 //cout に マニピュレーターを使う処理 省略 cout << setiosflags(flags) << setfill(fill); //設定復元 }
設定を保存する際、setfill だけは別で保存。(めんどくさい・・)
設定を保存しておくための型は ios::fmtflags という型
現在の設定を呼び出すメンバ関数は flags();
setfill() は、何か普通に char 型の変数に保存。setfill() の情報を取り出すのはメンバ関数 fill();
そして復元するには
setiosflags( ios::fmtflags ) ;
fill はそのまんま普通に setfill( );
現在の設定を呼び出すメンバ関数 flags( ); の引数に、先ほど保存した ios::fmtflags 型のオブジェクトをいれても設定できるらしい。
fill(); も同様。
void main() { ios::fmtflags flags = cout.flags(); //設定保存 char fill = cout.fill(); //設定保存 //cout に マニピュレーターを使う処理 省略 cout.flags(flags); //設定復元 cout.fill(fill); //設定復元 }
こんな感じ。
あと、マニピュレーターをつかうには #inclide <iomanip> が必要。
少しでも慣れるためにマニピュレーターを書いた
cin >> setw(width); //文字列入力の最大幅 cout << setw(width); //出力の最小値 cout << setfill(fill); //最小値に足りない部分をfillが埋める cout << setprecision(prec); // 少数の表示に関して、最高小数点以下prec桁まで表示 cin >> setiosflags(flags); // flags にまとめて設定 cout << setiosflags(flags); // 同上 cin >> resetiosflags(flags); //flags で指定した設定をクリアする cout << resetiosflags(flags); // 同上 cin >> setbase(base); // 基数を base に設定する(8,10,16のいずれか) cout << setbase(base); //同上 cout << boolalpha; //bool値を出力する際に true / false で出力する cout << noboolalpha; // 上の設定の解除 cout << showbase; // 16進数と8進数でそれぞれ頭に 0x 0 をつける cout << noshowbase; // 上の設定の解除 cout << showpoint; // 強制的に小数点を出力する cout << noshowpoint; // 上の設定の解除 cout << showpos; // 正符号(+)を出力する cout << noshowpos; // 上の設定の解除 cout << skipws; // 半角空白を無視 cout << noskipws; // 上の設定を解除 cout << uppercase; // 16進数のA~Fを大文字にする cout << nouppercase; // 上の設定を解除 cout << unitbuf; // バッファリングを無効 cout << nounitbuf; // 上の設定を解除 cin >> dec; // 10進数で入力 cout << dec;// 10進数で出力 cin >> hex; // 16進数で入力 cout << hex; // 16進数で出力 cin >> oct; // 8進数で入力 cout << oct; // 8進数で出力 cout << fixed; // 少数を必ず指数表記を使わずに出力する cout << scientific; // 少数を必ず指数表記で出力する cout << left; // 左詰めで出力 cout << right; // 右詰めで出力 cout << internal; // 符号は左に、その他は右詰めで出力 cin >> ws; // noskipwsを使用中なら有効。空白文字(タブ・改行を含む)を飛ばす。 cout << endl; // 改行を出力してフラッシュする cout << flush; // フラッシュする cout << ends; // ヌル文字を出力する
さらに、マニピュレーターを使う際の便利なやり方。
引数と戻り値にistream& や ostream& を使った関数を作ると、その関数名がマニピュレーターとなるらしい。
ostream& TestManip(ostream& testmanip) { // ostream& を引数と戻り値とした関数 return testmanip << setfill('0') << hex << uppercase; //設定だけする } cout << TestManip; //纏めて適用出来る
これは参照を返す関数になっている。
参照を返す関数についてはまたあとでやるらしい。
今回はここまで。