C++スタイルの入出力 (I/O stream)

Cではコンソール入出力の際に,printf()やscanf()といった関数を使っていました. C++でも,これらの入出力関数を使うこともできますが,新しいC++スタイルの入出力演算子(>>および<<)を使用することもできます. ここでは,C++の入出力に関連する内容を説明します.

練習課題

レポートプログラムの雛形ではC言語の入出力関数(scanfとprintf)を使用している. これらを全てC++言語の入出力演算子と定義済みストリームを使用した記述に書き換え,実行せよ. (main.cppの16,17行目,およびMyString.cppのprintString())

解説1: 最も簡単なコンソール出力のサンプルプログラム

LIST1-1 (console1.cpp)

LIST1-1

解説1-1)C++ヘッダ (#include<iostream>とは)

C言語のプログラムでは,ライブラリ関数を使うときにはヘッダファイルをインクルードしていました.例えば,入出力関数用ヘッダファイルのインクルードの際には,プログラムの先頭に以下のように書きました.

#include <stdio.h>

C++でも,このような書き方がサポートされています.しかし,標準C++では,さらに,新しい種類のヘッダが追加されました.新しいスタイルでは,ヘッダファイルではなく,識別子を指定します.".h"が無いことに注意してください.新しいスタイルのヘッダの例として以下のようなものがあります.

<iostream>   入出力関連ヘッダ

<fstream>   ファイル入出力関連ヘッダ

※ 識別子とは,変数,関数,クラスなどを個々に識別するためにつける名前のことです.

解説1-2)std名前空間 "namespace"

C言語では,開発するソフトウェアの規模が大きくなるにつれ,他の関数や変数と同じ名前を使用してしまう"名前の競合(もしくは衝突)"の問題が生じました.

C++における"名前空間"は,名前の競合を防ぐ目的で,識別子の名前を局所化するために使用されます.同じ"田中さん"でも"宇都宮の田中さん"と"小山の田中さん"というようにして区別することができますね.この"宇都宮","小山"が名前空間に相当すると考えてください.

さて,Cではライブラリ関数の名前などは,グローバルな名前空間に置かれていました.

新しいスタイルのC++ヘッダの内容は std名前空間に置かれています.std名前空間の関数や変数を使用するには「std::cout」のようにスコープ解決演算子を用いる必要があります.

ここで現れた「std::cout」は,C言語でいうstdout(標準出力=コンソール出力:console out)に当たります.

また,「std::endl」は,改行(end of line)を示します.

解説1-3)出力演算子 << と入力演算子 >>

C++では出力演算子 << と 入力演算子 >> が定義されています.Cでは<<は左シフト,>>は右シフトでした.C++ではシフト演算子としても使用されますが,入出力の演算子としての役割もあります

どちらの機能になるかは,演算子の左側におかれたオブジェクトの型に依ります.演算子の左が数値型であればシフト,入出力ストリームオブジェクトであれば入出力演算になります.これが「演算子 << が多重定義されている」ということの意味です.

なお,C++の入出力演算子を使うには,<iostream>ヘッダをインクルードする必要があります.

解説2: using namespace"を使用したコンソール出力のサンプルプログラム

LIST1-2 (console2.cpp)

LIST1-2

LIST1-1との違いは,何でしょうか?

解説2-1)"using namespace" について

以下の文を使用して,std 名前空間をデフォルトで参照可能(可視状態)にすることが出来ます.

using namespace std;

こうすることで,毎回スコープ解決演算子を用いて"std::"と書く必要がなくなります.

つまり,C++スタイルの入出力を使用する場合には,通常,プログラムの先頭に以下のように2行を記述することになります.

#include <iostream>
using namespace std;

もし,C++のコンパイラが古いバージョンで,上記の書き方ではエラーが発生する場合には,次のように1行で書くこともできます.

#include <iostream.h>

この場合はcoutなどはグローバル名前空間におかれます.

.h が付いている点とusing namespace ...の文がない点に注目してください.

解説3: 少し複雑なコンソール入出力のサンプルプログラム

LIST1-3 (console3.cpp)

LIST1-3

実行例

整数値を入力してください 5
あなたが入力したのは 5 です
実数値を入力してください 3.141592
あなたが入力したのは ******3.14 です
1文字だけ入力してください a
あなたが入力したのは a です
文字列を入力してください hello
あなたが入力したのは hello です

注目ポイント

入出力演算子(<<および>>)の右側に,文字列や数値の変数を置くことが出来る,ということに注目してください.

これも「演算子 << が多重定義されている」ということの意味です.

右辺に置いた変数(オブジェクト)の型に応じて,適切に表示してくれる,というのがC++スタイル入出力の良いところです.

一方,Cスタイルのprintfでは,書式文字列(%d,%fや%s)と,引数の型(int型,float型,文字列char[]型)が違うとおかしな表示になります.

解説3-1)C++の定義済みストリーム

ストリーム(stream)という英単語の意味は”流れ”ですね.ここでは,情報を流すための論理デバイスのことを言います.論理デバイスが,CやC++の入出力システムによって物理デバイス(キーボードや画面)とリンクされることでデータを入力したり,表示したりすることができるわけです.

Cでは,出力の際に,以下のように記述していました.

int i;

double d;

fprintf(stdout, "あなたが入力したのは %d です\n", i);

fprintf(stdout, "あなたが入力したのは %lf です\n", d);

printf("あなたが入力したのは %d です\n", i);

printf("あなたが入力したのは %lf です\n", d);

これを,C++では以下のように記述することができます.

cout << "あなたが入力したのは" << i << "です\n";

cout << "あなたが入力したのは" << d << "です" << endl;

書式指定子(%d,%lf など)がないですね. 書式指定子を気にしないでよいのはちょっと嬉しいですね.

C++の定義済みストリームを以下の表にまとめます.

  意味 デフォルトデバイス Cとの対応
cin 標準入力 キーボード stdin
cout 標準出力 画面 stdout
cerr 標準エラー出力 画面 stderr
clog バッファを使うcerr 画面  

解説3-2)入出力マニピュレータ (manipulator:処理子,操作子)

入出力マニピュレータ(I/O manipulator)とは,入出力ストリームに対して使用できるオブジェクトです.先ほど登場した endl は改行文字(\n)を出力し,ストリームをフラッシュする入出力マニピュレータです.よく使用する入出力マニピュレータと,その使用例,実行結果を以下の表にまとめます.

なお,setw()のように仮引数をとるマニピュレータを使用するには<iomanip>をインクルードしておく必要があります.

  目的 実行結果
endl 改行文字(\n)を出力し,
ストリームをフラッシュ
cout << "hello" << endl; hello
hex hexフラグをオン cout << hex << 255; ff
oct octフラグをオン cout << oct << 9; 11
setw(int w) フィールド幅をwに設定 cout << setw(5) << 35; ___35 ※注
setprecision(int p) 精度の桁数をpに設定 cout << setprecision(3) << 2.5614; 2.56
setfill(int ch) 充填文字をchに設定 cout << setfill('*') << setw(4) << 5; ***5

※注 ___ は半角スペース3つを表す.

ファイル入出力について

ファイル入出力の必要がある場合,入力ファイルが1つ・出力ファイルが1つまでであれば,コンソール入出力をファイルにつなぎかえる「リダイレクト機能」により,大抵の用事は済ますことが出来ます.リダイレクトの復習(プログラミング演習Iデバッガ)

ただし,ファイルを複数扱う場合や,ファイル中の場所を指定して一部だけを読み書きしたい場合には,プログラムの中でファイルを扱うための「ファイル入出力」の機能を使用する必要があります.