非常に多くの機能がある巨大な言語だが、オブジェクト指向言語として クラスの機能を付け加えたのが最大の変更と言えよう。 (最初期はC with classesと呼ばれていた)
とても全体は説明しきれないが、わかりやすいところから簡単に紹介していく。
#include <iostream> int main() { std::cout << "Hello world" << std::endl; }
ソースファイルの拡張子は.cpp, .cc, .cxxなどいろいろある。 コンパイルは、c++ hello.cpp。
printfの代わりに「cout << 変数名」を使う。endlは改行。 「#include <iostream>」はcoutを使うのに必要。
printfを使うことも可能で、その場合は「#include <cstdio>」とする。
「std::」は、「stdという名前空間に属する」という意味。C++は巨大なので、 名前の衝突を避けるためにいろいろな名前を「名前空間」に分類する機能がある。 いちいち付けるのが面倒ならば、
using namespace std;と書くと、グローバルな名前空間にstd内の名前を引っ張り出すことも出来る。
#include <iostream> class test { private: int age; int height; public: void setage(int x) { age = x; } void setheight(int x) { height = x; } void print() { std::cout << "(" << age << "," << height << ")" << std::endl; } }; int main() { test t; t.setage(20); t.setheight(170); t.print(); }
C++では、構造体の機能を拡張してクラスを実現している。 上の例は、
struct test { int age; int height; };という構造体の宣言に、あれこれ加わったものと見てほしい。 つまり、これは構造体testの宣言と、その構造体を操作する関数 setage, setheight, printを一まとめにしたもの。
あえてC言語で同じ動作をするプログラムを書けば、
#include <stdio.h> struct test { int age; int height; }; void test_setage(struct test* s, int x) { s->age = x; } void test_setheight(struct test* s, int x) { s->height = x; } void test_print(struct test* s) { printf("(%d,%d)\n", s->age, s->height); } int main() { struct test t; test_setage(&t, 20); test_setheight(&t, 170); test_print(&t); }
のようになるだろうか。 mainからの呼び出し方から、Cでの「主体は関数」と、 C++での、「主体はデータで関数はそのデータに付随する」という 考え方の違いが感じられるだろうか。 一つのプログラムの中で何種類もの構造体(クラス)を使うようになると、 Cだとどの関数がどの構造体を操作するものか混乱しがちだが、 C++だとすっきり書けるようになる。
private:, public:もC++の機能で、private指定の部分は外部から直接 アクセス出来なくなる。これにより、メンバ変数はpublic指定された 関数(メンバ関数)経由でしか操作されないので、プログラムの安全性が増す。
コンストラクタ、デストラクタなど、クラス関連の重要な知識は まだたくさんあるが、とりあえずここまで。 既存のクラスを一部変更したり機能を追加したりして新しいクラスを作る、 「継承」の概念も重要。
#include <iostream> int addtwo(int x, int y) { return x + y; } int addtwo(double x, double y) { return x + y; } int main() { int a=1, b=2; double x=2., y=3.; std::cout<< addtwo(a, b) << std::endl; std::cout<< addtwo(x, y) << std::endl; }
内部で自動的にaddtwo_int_intやaddtwo_double_doubleのような名前に 置き換えられている、と考えれば分かりやすいかも。
int swap(int &x, int &y) { int tmp; tmp = x; x = y; y = tmp; } int main() { int a=1, b=2; swap(a, b); }
関数の引数の「int &x」が参照型。 C言語で同じものを書けば、
int swap(int *x, int *y) { int tmp; tmp = *x; *x = *y; *y = tmp; } int main() { int a=1, b=2; swap(&a, &b); }
のようになる。通常変数から参照型へコピーするときは自動で 「&」が付き、参照型を使うときは自動で「*」が付くと思えば理解しやすい。
#include <iostream> class hoge { public: int a; int operator+(int x) { return a + x; } }; class fuga { public: int a; int operator+(int x) { return a - x; } }; int main() { hoge p; fuga q; p.a = 3; q.a = 3; std::cout << p + 2 << std::endl; std::cout << q + 2 << std::endl; }
hogeとfugaでそれぞれ異なるmethod(関数)が呼び出されているのが分かる。
これを応用すると、例えば複素数クラスのようなものを作成し、 複素数型を「あたかも初めから組み込まれていた型であるかのように」 振舞わせることが出来る。
詳細に興味があれば、C++での演算子多重定義を 見て欲しい。