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

< C++


C++とは編集

 
ビャーネ・ストロヴストルップ

C++は、1979年、 米国ニュージャージー州マレーヒルのベル研究所で、 ビャーネ・ストロヴストルップによって開発された、 オブジェクト指向型のプログラミング言語である。 何度かの改訂、 アレクサンダー・ステパノフによる標準テンプレートライブラリ(STL)の作成を経て、 1994年7月のANSI/ISO委員会で最終承認標準化されている[1]

その後も、ISO(国際標準化機構)/()IEC(国際電気標準会議) ()JTC 1(第一合同技術委員会)/SC 22(プログラミング言語標準化副委員会) WG21(C++ワーキンググループ)

  • C++98 ISO/IEC 14882:1998
  • C++03 ISO/IEC 14882:2003
  • C++11 ISO/IEC 14882:2011
  • C++14 ISO/IEC 14882:2014
  • C++17 ISO/IEC 14882:2017
  • C++20 ISO/IEC 14882:2020
  • C++23 ISO/IEC 14882:2023(未発行)

と改定をつづけている。

C++は、Cを拡張してより使いやすい言語にすることを目指して作成された。 実際には、現在でもCは非常に人気のある言語であり続けている。 このことには、様々な理由が考えられるが、 例としては、

  1. Cはよく用いられる言語なので、他人のコードを読むためには、Cを勉強する必要がある。
  2. C++は、Cよりも機能が多く、勉強するのに時間がかかる。
  3. C++が提供する機能は、ある程度大きいコード以外ではあまり用いる機会がない。

等があげられる。

C++が提供するCの拡張として以下の機能があげられる。 これらは、Cを用いる上でたびたび現れる問題に対応するために導入された。

  1. クラスの定義や継承[2]
  2. 関数や演算子のオーバーロード[2]
  3. テンプレート[2]
  4. 仮想関数[2]
  5. 例外処理[2]
  6. 名前空間[2]
  7. 型推論[3]
  8. ラムダ関数[3]

CとC++の相違点編集

要点編集

  1. Cでは引数がない関数の関数原型の引数のリストにはvoidを指定する。一方、C++ではvoidの指定を省略できる。
  2. Cでは関数原型の記述は推奨されるが任意である。一方、C++では必ず記述しなければならない。
  3. Cでは関数の戻り値の型がvoid以外の時でもreturn文で値を指定しなくともよい。一方、C++では必ず指定しなければならない[4]
  4. Cでは関数の戻り値の型を省略すると整数型が使われる。一方、C++では必ず型を指定しなければならない。
  5. Cではかつてローカル変数の宣言はブロックの先頭で行う必要があった(この制限は、C99 で撤廃された)。一方、C++ではどこで行ってもよい。
  6. Cではbool型が標準設定には無い(C99で_Bool型がキーワードに追加されたが、ヘッダー stdbool.h をインクルードする必要がある)。一方、C++では何らヘッダーをインクルードすることなくbool型を使える。

Cの基礎知識については、『C言語/基礎知識』を参照してください。 ただしprintf関数とscanf関数については、後述する#コンソール入出力で、より改善されたとC++開発者が主張する方法を用いるため、学習する必要はあまりありません。

Hello, World!を実行する編集

はじめてソースコードを書くときは、 まず「hello, world」プログラムと呼ばれる 画面に「hello, world(改行)」とだけ表示するプログラムを、 書くことにしましょう。

C++はCを拡張して作られているため、 Cと似た部分が多くあります。 例えば、次のCで書かれたプログラムはC++でも正しいプログラムです。 このプログラムの解説はC言語/はじめに#Hello, World!を実行するを参照してください。

#include <stdio.h>

int main(void)
{
	printf("hello, world\n");
	return 0;
}

しかし、C++では、一部機能が追加されて、また入出力はより直観的な方法を使うことができるようになりました。 以下は同じ内容をC++の機能を用いて書き直したものです。

#include <iostream>
using namespace std;

int main() 
{
    cout << "hello, world" << endl;
    return 0;
}

ここではこのソースコードについて簡単に説明します。

  • 1行目は、新しいスタイルのヘッダです。
    Cでは#include <stdio.h>のようにファイル名を指定しましたが、これは古いスタイルのヘッダで、C++では新しいスタイルのヘッダを使い、標準識別子を指定します。新しいスタイルのヘッダは、ファイル名ではないので「.h」拡張子がありません。古いスタイルのヘッダは、引き続き使用できますが、推奨されません。標準Cヘッダを新しいスタイルで書くと、頭にcが付きます。例えば、#include <stdio.h>#include <cstdio>となります。
  • 2行目は、std名前空間で定義された識別子(この場合は cout 及び endl)を std:: 前置句なしに参照可能にしています。
  • 3行目は、任意の空白行です。
  • 4行目から8行目は、main関数の定義です。
  • 5行目から8行目は、ブロックというまとまりです。
  • 6行目は、出力演算子による出力操作です。
    Cではコンソール出力に、printf関数を用いましたが、C++では「<<演算子」を使って、改善された方法で出力を行うことができます。Cでは<<は左シフト演算子でしたが、C++ではその機能に加えて、出力演算子として用いることができます。これは後で解説する演算子のオーバーロードの例でもあります。
  • 7行目は、return文です。
  • 6と7行目が、空白によって右側に偏っているのは、インデントのためです。

オブジェクト指向との関連編集

クラスを用いたプログラムをオブジェクト指向プログラムと呼びます[5]。 そのため、上のC++のhello, worldの例はオブジェクト指向プログラムの例です。 この例で用いられているオブジェクト指向について簡単にまとめます。

この例では、オブジェクト指向を導入した結果として、

関数printfを、文字列 "hello, world" を引数として呼出す

という考えから、

cout に、文字列 "hello, world" を送る

という考えへの移行が行われています。

この cout はストリーム操作を抽象化したものですが、この様な抽象化された実体をオブジェクトと呼びます。 例えば、Cでは標準出力に出力する時には次のようにprintf関数を使いました。

printf("abc");

一方、ファイルに出力する時には次のようにfprintfという異なる関数を使う必要があります。

fprintf(f, "abc");  // fはFILE型へのポインタ

これは、扱う対象が異なっている以上当然ですが、出力する対象の数が増えて来るとわかりづらくなります[6]

一方、C++を用いると、標準出力に対しては、

std::cout << "abc";

ファイルに対しても、

fs << "abc"; // fsはファイルストリーム

となり、同じ名前の関数 operator<< を用いることが出来ます。

ここで、 operator<< は、引数のオブジェクト(実際にはオブジェクトが所属するクラス)によって動作が変化していることが重要です。 このように、演算子を含む関数がオブジェクトの種類によって振舞いを変えることで、様々なデータに対する操作を統一的に扱うことが出来ます。

演算子のオーバーロード編集

C++では、演算子のオーバーロードによって定義型に演算子を追加することができます。

cout << "abc";
operator<<(cout, "abc");

という関数呼出しと等価です(実際に動くコードです)。

演算子のオーバーロードは、「新しい数値型」を定義する場合などでは素晴らしい効果を発揮しますが、ファイルストリームにシフト演算子を適用するようなケースは「予め了解された体系」ではないので賛否両論があります。

オブジェクト指向編集

オブジェクト指向プログラミング(Object-Oriented Programming : OOP)は、 コンピュータプログラムの構造、構成法の一つで、 関連するデータの集合体と、 それを操作する手続きを「オブジェクト」(object)と呼ばれるひとまとまりの単位として一体化し、 オブジェクトの組み合わせとしてプログラムを記述する手法である。 [7]

プログラミングでは、どんどん複雑になっていくプログラムに対応するために、新しい手法が次々と作られてきた。

最初は、フロントパネルのスイッチをオンオフすることによって、ごく短いプログラムが作成された。

次に、アセンブリ言語によって、より長いプログラムが作成できるようになった。

そして、1950年代、最初の高レベル言語(FORTRAN)が開発されたことによって、数千行にわたるプログラムが作成できるようになった。 しかし、何でも許される方式であったため、大きなプログラムを書こうとすると、読みづらく保守しにくい「スパゲティコード」となってしまった。

1960年代には、構造化プログラミング言語(structured programming language)の登場で、スパゲティコードを排除し、五万行に及ぶプログラムを作成し、保守できるようになった。

Cも広い意味では構造化プログラミング言語である。構造化プログラミング言語では、制御構造を使い、プログラムを構成要素に分割して記述する。

しかし、構造化プログラミング言語も、プログラムの長さが一定に達すると限界に達するだろうと危惧されるようになった(しかし現実は2020年でも、たとえばLinuxのカーネルは標準C言語で記述されているが)。

オブジェクト指向プログラミングでは、前述したとおり、プログラムを自己完結したオブジェクトとして記述することで、複雑さを軽減し、より巨大なプログラムを記述できるようにした。

C++におけるオブジェクト指向プログラミングには、カプセル化、ポリモーフィズム、継承の3つの特色がある。 これらについて説明していく。

カプセル化編集

カプセル化(encapsulation)とは、 データとそれを操作する手続きを一体化して「オブジェクト」として定義し、 オブジェクト内の細かい仕様や構造を外部から隠蔽することである。 [8]

手続きとデータを一体化し、手続きによってしかデータを操作できないようにすることで、ブラックボックスなオブジェクトを作成し、独立性を高めている。

さらにC++におけるカプセル化では、データと手続きを、非公開(private)と公開(public)に分けて、 オブジェクトの外部からは、非公開のものにはアクセスできず、公開のもののみにアクセスできるようにしている。

カプセル化によって、保守性や開発効率を挙げることができ、コードの再利用も簡単になる。

ポリモーフィズム編集

ポリモーフィズム(polymorphism、多相性、多態性、多様性)とは、同名のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のことである[9]

ポリモーフィズムでは、同一の名前で複数の異なる動作を作成し使うことができる。

例えば、Cで絶対値を求めるには、引数のデータ型に合わせて abs(), labs(), llabs(), fabsf(), fabs(), fabsl(), cabsf(), cabs(), cabsl() の9つの関数を使い分ける必要がある[10]。 一方、C++で絶対値を求めるには、引数がどのデータ型であっても std::abs() という1つの関数で求めることができる。

このように1つの関数名を複数の異なる目的で使うことを、関数のオーバーロード(function overloading)という。

ポリモーフィズムの利点は、メソッド名を統一することで記述ミスを減らせるなどが挙げられる。

継承編集

継承(inheritance)とは、 既に定義されているクラスをもとに、拡張や変更を加えた新しいクラスを定義することである。 [11]

継承を使うことで、 あるものの性質を受け継ぎ独自の機能を追加することができる。

例えば、哺乳類を継承し人、犬、猫を新たに作るといった考え方ができる。

関数などによってもコードの再利用は可能だが、さらに継承によって、より自由にコードの再利用ができるようになる。

継承には「カプセル化を破壊する」との批判もあり、継承あるいは仮想メンバー関数のオーバーライドを禁止する修飾子 final が生まれた。

より新興のオブジェクト指向言語 Kotlin では、class は open 修飾子で明示的に継承を許可されなければ、継承することが出来ない。


脚注編集

  1. ^ The Standard Template Library Alexander Stepanov October 31, 1995
  2. ^ 2.0 2.1 2.2 2.3 2.4 2.5 C++言語とは”. IT用語辞典 e-Words. 2021年8月12日閲覧。
  3. ^ 3.0 3.1 C++14登場 -その新機能”. InfoQ. 2021年8月12日閲覧。
  4. ^ main()の戻り値がintであった場合に限り省略可能で return 0; が仮定される。
  5. ^ オブジェクト指向プログラミングには、クラスを用いずプロトタイプを用いる別の手法もあります。
  6. ^
    printf("abc");
    
    fprintf(stdout, "abc");
    
    と等価で、他にも sprintf() などもあり、手続き型言語なりの派生体系が作られています。
    そもそも型FILEは、内部構造の隠蔽が図られている考えられています。
  7. ^ オブジェクト指向プログラミングとは|OOP|Object Oriented Programming - 意味/定義 : IT用語辞典
  8. ^ カプセル化とは|encapsulation - 意味/定義 : IT用語辞典
  9. ^ ポリモーフィズムとは|polymorphism|ポリモルフィズム|多相性 - 意味/定義 : IT用語辞典
  10. ^ C言語でも、C99から総称型数学関数<tgmath.h>を使えば引数の型に応じて対応する数学関数が呼び出される。
  11. ^ 継承とは|inheritance|インヘリタンス - 意味/定義 : IT用語辞典

参考文献編集

  • 神林靖 監修 Herbert Schildt著『独習C++ 第3版』トップスタジオ訳、翔泳社、2005年11月05日 第3版第10刷発行