第43回 プログラミングについて『 C++の変なところ』

UNIXでは現在でもC言語が主流ですが、パソコンではOSがWindowsが主流(マック党の方には申し訳ありません)になりC++がC言語よりも使われるようになってきました。マイクロソフトは会長のビルゲイツがBASICで会社の基礎を築いたこともあってBASICに力を入れているようです。そこでこれからは少しづつでもC++について触れていくことにしましょう。
これまでにもC++については名前だけは出していましたが、その実態についてはあまり触れてはいませんでした。実際に私もコンパイラはC++のものであっても、Windowsのクラスライブラリを使うプログラムを書くとき以外はC++本来の醍醐味であるオブジェクトはあまり使っていません。
C言語を使われている方が、C++を始めようとしたときに最初から難しいことを勉強してしまって挫折してしまうことが多いようです。C++からすればC言語はそのサブセットと考えても差し支えないので、最初は簡単で便利な機能から使い始めることをお勧めします。
まず一番頭を使わない上に便利なのは、コメントです。C言語でのコメントは /* */の形式しかありませんでしたが、C++ではこのコメントの記述方法以外に、 // を使用できるようになっています。この記述方法は1つの行で // 以降はコメントとして記述できます。ですから次のようになります。
/* コメント */
/*
コメント
*/
//コメント
//
コメントではありません
//
これだけではC++を使っているんだという感じはあまりしないかも知れません。
次に入出力の例を見てC++の変なところを感じてみましょう。C++では標準出力への出力や標準入力からの読み込みはC言語での標準関数も勿論使えますが、 cout とcin という変なものがあります。C++では通常これは何気なく使っているのですが、実際はこれもオブジェクトなのです。
#include <iostream.h>
void main()
{
cout << "test" << endl;
}
これを実行してみると、
test
と表示されます。次に、
#include <iostream.h>
void main()
{
cout << 100 << endl;
}
では、
100
と表示されます。もう1つ、
#include <iostream.h>
void main()
{
cout << 0.5 << endl;
}
は、
0.5
と表示されます。つまり cout は標準出力であり、 cout << "test" は、文字列 testに対して cout に出ろ!と命令していると考えればいいでしょう。cout << 0.5 も同様に実数値 0.5 に対して cout に出ろ!と命令しているのです。endl はこれで1行の終わりで、改行を行ないます。
ですから、cout << "test" << " " << 100 << " " << 0.5 << endl; とすると、
"test" 、" "、100、" "、0.5、改行の順番で標準出力に出ろ!ということになります。
これだけではつまらないので、数値 100 を10桁右詰めで表示してみましょう。
#include <iostream.h>
#include <iomanip.h>
void main()
{
cout << setw(10) << 100 << "|" << endl;
}
こうすると、
100|
となります。これは、数値 100 に対して10桁になって標準出力にでろ!と命令しているのです。左詰めで10桁にするには、
#include <iostream.h>
#include <iomanip.h>
void main()
{
cout <<setiosflags(ios::left);
cout << setw(10) << 100 << "|" << endl;
}
とすると、
100 |
となります。16進数で表示するには、
#include <iostream.h>
#include <iomanip.h>
void main()
{
cout << hex << 100 << endl;
}
とします。例はこのくらいにしましょう。これらの例を見てみると、cout は特別な機能を持ったものに感じるかも知れませんが、これはC++の機能なのです。ちょっと話は難しくなるのですが、cout はもともとは iostream というクラスから派生した1つのオブジェクトで、iostream.h をインクルードすると静的に定義されているこのオブジェクトを使用できるのです。演算子 << は cout に対しての特別なものではなく、cout のクラスに演算子として定義してあるものなのです。
同じようにして読み込みの例も見てみましょう。
#include <iostream.h>
#include <iomanip.h>
void main()
{
char text[100];
cin >> text;
cout << text << endl;
}
このようにして実行すると、標準入力からの読み込みになり、abcde とキー入力すると、abcde と表示されます。
#include <iostream.h>
#include <iomanip.h>
void main()
{
double d
cin >> d;
cout << d << endl;
}
このプログラムを実行すると、標準入力からの読み込みになり、123.45 とキー入力すると 、123.45 と表示されます。
このように見てみると、cout や cin は何を出力するのか、または何に読み込むのかをきちんと判断しているのが分かります。printf 関数を使って次のようにすると、
printf("%d\n",123.45);
%d で実数値を表示しようとしてしまうので希望した答えと違った結果になってしまうのに対し、C++でこの cout を使うと勝手に出力の方法が適正なものになるのです。ここが手続き指向とオブジェクト指向の違いで、
printf("%d\n",123.45);
では、printf 関数に手続き(この場合は書式)とデータ(この場合は 123.45)を渡しているのに対し、
cout << 123.45 << endl;
は、123.45 と endl を cout に送り込んでいるだけで書式は何も指定しません。coutは何を表示するかを判断して書式を決めている訳です。つまり、cout はデータの型に対して表示するための文字列への変換の方法を知っている変な奴、つまりオブジェクトなのです。
それでは機能は殆どありませんが、この cout を printf を使って作ってみましょう。
#include <stdio.h>
#define ENDL "\n"
class out_class{
public:
out_class& operator << (int i);
out_class& operator << (char *s);
};
class out_class out;
out_class& out_class::operator << (int i)
{
printf("%d",i);
return *this;
}
out_class& out_class::operator << (char *s)
{
printf("%s",s);
return *this;
}
void main()
{
out << 100 ;
out << "test";
out << ENDL;
out << 200 << "test2" << ENDL;
}
このプログラムを実行すると、
100test
200test2
と表示されます。今回は詳しい説明はしませんが、おおよその動作を説明すると、
class out_class{
public:
out_class& operator << (int i);
out_class& operator << (char *s);
};
の部分では、 out_class を宣言しています。クラス out_class ではこのクラスと整数との演算子 <<、文字列との演算子を宣言しています。
class out_class out;
は、クラス out_class のオブジェクト out を実際に作っています。
out_class& out_class::operator << (int i)
{
printf("%d",i);
return *this;
}
out_class& out_class::operator << (char *s)
{
printf("%s",s);
return *this;
}
は、整数と文字列に対する演算子の本体を定義しています。
out << 100 ;
は、out と 100 との演算を行ないます。実際には演算というよりも、
out_class& out_class::operator << (int i){ ... }
の関数が呼び出されているのです。
今回はC++の変なところとその実態を垣間見てみました。あくまでもC言語とかなりかけはなれている部分ですので、今回の内容はあまり真剣にならならなくても構いません。フーン変なんだ... 程度に感じて下されれば結構です。
それではまた次回。