C++/C++特有の概念

C++は、1979年、米国ニュージャージー州マレーヒルのベル研究所で、ビャーネ・ストロヴストルップによって開発された、オブジェクト指向プログラミング言語である。 数回の改訂を経て、アレクサンダー・ステパノフによって初期の設計が行われ、その後多くの人々によって改良が加えられた標準テンプレートライブラリ(STL)の作成も行われ、1994年7月のANSI/ISO委員会で最終承認標準化された。
その後も、ISO/IEC JTC 1/SC 22 WG21 は
と改定をつづけている。
C++は、Cを拡張してより使いやすい言語にすることを目指して作成された。 実際には、現在でもCは非常に人気のある言語であり続けている。 このことには、様々な理由が考えられるが、 例としては、
等があげられる。
C++が提供するCの拡張として以下の機能があげられる。 これらは、Cを用いる上でたびたび現れる問題に対応するために導入された。
Cの基礎知識については、『C言語/基礎知識』を参照してください。 ただしprintf関数とscanf関数については、後述する#コンソール入出力で、より改善されたとC++開発者が主張する方法を用いるため、学習する必要はあまりありません。
はじめてソースコードを書くときは、 まず「hello, world」プログラムと呼ばれる 画面に「hello, world(改行)」とだけ表示するプログラムを、 書くことにしましょう。
C++はCを拡張して作られているため、 Cと似た部分が多くあります。 例えば、次のCで書かれたプログラムはC++でも正しいプログラムです。 このプログラムの解説はC言語/はじめに#Hello, World!を実行するを参照してください。
しかし、C++では、一部機能が追加されて、また入出力はより直観的な方法を使うことができるようになりました。 以下は同じ内容をC++の機能を用いて書き直したものです。
ここではこのソースコードについて簡単に説明します。
クラスを用いたプログラムをオブジェクト指向プログラムと呼びます。 そのため、上のC++のhello, worldの例はオブジェクト指向プログラムの例です。 この例で用いられているオブジェクト指向について簡単にまとめます。
この例では、オブジェクト指向を導入した結果として、
という考えから、
という考えへの移行が行われています。
この cout はストリーム操作を抽象化したものですが、この様な抽象化された実体をオブジェクトと呼びます。 例えば、Cでは標準出力に出力する時には次のようにprintf関数を使いました。
一方、ファイルに出力する時には次のようにfprintfという異なる関数を使う必要があります。
これは、扱う対象が異なっている以上当然ですが、出力する対象の数が増えて来るとわかりづらくなります。
一方、C++を用いると、標準出力に対しては、
ファイルに対しても、
となり、同じ名前の関数 operator<< を用いることが出来ます。
ここで、 operator<< は、引数のオブジェクト(実際にはオブジェクトが所属するクラス)によって動作が変化していることが重要です。 このように、演算子を含む関数がオブジェクトの種類によって振舞いを変えることで、様々なデータに対する操作を統一的に扱うことが出来ます。
C++では、演算子のオーバーロードによって定義型に演算子を追加することができます。
という関数呼出しと等価です(実際に動くコードです)。
演算子のオーバーロードは、「新しい数値型」を定義する場合などでは素晴らしい効果を発揮しますが、ファイルストリームにシフト演算子を適用するようなケースは「予め了解された体系」ではないので賛否両論があります。
オブジェクト指向プログラミング(Object-Oriented Programming : OOP)は、 コンピュータプログラムの構造、構成法の一つで、 関連するデータの集合体と、 それを操作する手続きを「オブジェクト」(object)と呼ばれるひとまとまりの単位として一体化し、 オブジェクトの組み合わせとしてプログラムを記述する手法である。
プログラミングでは、どんどん複雑になっていくプログラムに対応するために、新しい手法が次々と作られてきた。
最初は、フロントパネルのスイッチをオンオフすることによって、ごく短いプログラムが作成された。
次に、アセンブリ言語によって、より長いプログラムが作成できるようになった。
そして、1950年代、最初の高レベル言語(FORTRAN)が開発されたことによって、数千行にわたるプログラムが作成できるようになった。 しかし、何でも許される方式であったため、大きなプログラムを書こうとすると、読みづらく保守しにくい「スパゲティコード」となってしまった。
1960年代には、構造化プログラミング言語(structured programming language)の登場で、スパゲティコードを排除し、五万行に及ぶプログラムを作成し、保守できるようになった。
Cも広い意味では構造化プログラミング言語である。構造化プログラミング言語では、制御構造を使い、プログラムを構成要素に分割して記述する。
しかし、構造化プログラミング言語も、プログラムの長さが一定に達すると限界に達するだろうと危惧されるようになった(しかし現実は2020年でも、たとえばLinuxのカーネルは標準C言語で記述されているが)。
オブジェクト指向プログラミングでは、前述したとおり、プログラムを自己完結したオブジェクトとして記述することで、複雑さを軽減し、より巨大なプログラムを記述できるようにした。
C++におけるオブジェクト指向プログラミングには、カプセル化、ポリモーフィズム、継承の3つの特色がある。 これらについて説明していく。
カプセル化(encapsulation)とは、 データとそれを操作する手続きを一体化して「オブジェクト」として定義し、 オブジェクト内の細かい仕様や構造を外部から隠蔽することである。
手続きとデータを一体化し、手続きによってしかデータを操作できないようにすることで、ブラックボックスなオブジェクトを作成し、独立性を高めている。
さらにC++におけるカプセル化では、データと手続きを、非公開(private)と公開(public)に分けて、 オブジェクトの外部からは、非公開のものにはアクセスできず、公開のもののみにアクセスできるようにしている。
カプセル化によって、保守性や開発効率を挙げることができ、コードの再利用も簡単になる。
ポリモーフィズム(polymorphism、多相性、多態性、多様性)とは、同名のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のことである
ポリモーフィズムでは、同一の名前で複数の異なる動作を作成し使うことができる。
例えば、Cで絶対値を求めるには、引数のデータ型に合わせて abs(), labs(), llabs(), fabsf(), fabs(), fabsl(), cabsf(), cabs(), cabsl() の9つの関数を使い分ける必要がある。 一方、C++で絶対値を求めるには、引数がどのデータ型であっても std::abs() という1つの関数で求めることができる。
このように1つの関数名を複数の異なる目的で使うことを、関数のオーバーロード(function overloading)という。
ポリモーフィズムの利点は、メソッド名を統一することで記述ミスを減らせるなどが挙げられる。
継承(inheritance)とは、 既に定義されているクラスをもとに、拡張や変更を加えた新しいクラスを定義することである。
継承を使うことで、 あるものの性質を受け継ぎ独自の機能を追加することができる。
例えば、哺乳類を継承し人、犬、猫を新たに作るといった考え方ができる。
関数などによってもコードの再利用は可能だが、さらに継承によって、より自由にコードの再利用ができるようになる。
継承には「カプセル化を破壊する」との批判もあり、継承あるいは仮想メンバー関数のオーバーライドを禁止する修飾子 final が生まれた。
より新興のオブジェクト指向言語 Kotlin では、class は open 修飾子で明示的に継承を許可されなければ、継承することが出来ない。