Assyのリベラル文学研究所もご覧ください。

C/C++

プログラムとは

プログラムとは、「コンピュータに対する命令の記述書」であると言えます。
たとえば、(3 + 1) × 4を計算するとします。これをプログラムにすると、

int x = 3 + 1;
int y = x * 4;
printf("%d\n", y);

のようにC言語で書くことができます。
ここでは、int型(整数値)の型を持つxとyという変数、+(足し算)と*(掛け算)という演算子、3と1と4という整数リテラル、そしてprintf()というC言語の関数を使っています。
まず、変数とは「値を入れる数」のことです。演算子とは「値と値を計算して別の値を返す命令」のことです。また、3や1や4は数値として「値」として処理されます。
これだけなら、数学にも出てくる内容であり、数式と何も変わりませんが、問題はprintf()関数です。この関数は、コンソール画面に変数の中身を表示しろ、という意味になります。
関数には、引数と呼ばれる「関数に渡す値」を指定して呼び出すことができます。printf()はフォーマットした文字列を表示することが可能なため、"%d\n"という引数を与えています。「"」と「"」で囲まれた値は文字列として処理されます。%dはprintf()で使用することのできる文字列フォーマットで、整数値の変数の値を取り出すことを表しています。そして、yの値が%dの中に展開されます。\nは改行コードです(後述します)。
これだけではつまらないので、これを10回表示させるようにしてみましょう。以下のようになります。

int i;
int x = 3 + 1;
int y = x * 4;
for (i = 0; i < 10; i++) {
    printf("%d\n", y);
}

for文は繰り返し構文で、iというインクリメント(順番に一つずつ増やしていく)するカウント用の変数を宣言し、for文で繰り返しprintf()関数を実行します。for文のパラメータは、左から

for (繰り返しの最初に行うこと; 繰り返しが行われる条件; 繰り返しがひとつ終わる度に実行すること) {
    繰り返す処理内容;
}

を意味しています。初期値としてi = 0;を代入し、iが10よりも小さい間中繰り返しを続け、繰り返しがひとつ終わる度にiを1ずつインクリメントする(i++)ことを表しています。i < 10は論理演算子で、「iが10よりも小さい」という条件式をfor文に指定できます。
プログラムとは、こうした「コンピュータにこういうことをしろ」という命令の、ひとつひとつの手順の記述に他なりません。その中で、数学で使われるような変数や、さまざまな文(条件分岐や繰り返し)や演算子が使えるほか、そして関数すなわちサブルーチン(ひとまとまりの処理)を作ったり、システムに存在するさまざまな関数、たとえばフォーマットを指定して出力するprintf()やコマンドラインからデータを入力して変数に格納するscanf()などを利用したりすることができます。
C言語では、文には、繰り返しを行うためのfor文のほか、条件に応じて分岐をするif ~ else文などがあります。また、変数の型には文字型を表すcharや浮動小数点数型を表すfloat/doubleなどがあります。変数のデータはメモリに格納されており、メモリアドレスによってアクセスすることのできるポインタという特殊な変数の型もあります。また、C++Javaなどはさらに高度な「オブジェクト指向」というプログラム設計の考え方があります。
変数には、整数型の変数なら整数、文字型の変数なら文字を格納します。型の許す範囲であれば、どんな値でも格納すること、そして変更したり取り出したりすることができる便利なしろものです。変数は一時的に格納したり、演算子によって計算に使ったり、条件分岐や繰り返しの際の条件式に使ったり、関数を実行する際にその関数の中に渡してその変数についてのルーチンを実行したりできます。(通常は関数に値として与えますが、ポインタを使うことで関数の内部から変数の値を変更できます。)
また、上のコードには改行コードを表す特殊文字エスケープシーケンス)である\nが使われています。上のコードでは、\がフォントの関係から¥と表示されていますが、この2つは同一文字です。\nを適切に入れることで、その場で次の行に改行してくれます。エスケープシーケンスは他にもあって、スペースを表す\s、タブを表す\t、null文字を表す\0があります。便利なのはタブで、文字の幅に合わせて自動的に伸縮してくれるスペースを表示できます。文字の幅が1つか2つ変わっても、同じ幅のスペース(通常は4か8)を保ってくれます。また、null文字は文字列の終端を表すのに使用します。
プログラミングとは、こうした「プログラムを開発する作業」のことです。思ったよりも簡単だと思われませんか?プログラミングはこのように、とても単純で簡単な作業です。

コマンドとGUIのプログラム

コマンドの基本は、C言語ではmain関数からプログラムが始まります。ここから全ての関数が呼ばれます。
あるいは、GUIプログラムでは、イベント駆動によるプログラムを行うことが多いです。ボタンやメニューにあらかじめ「クリックされたらこの関数を実行せよ」という「イベント」を登録し、そのイベントからそれぞれの関数(コールバック関数)が呼ばれます。
Windows APIなどの低レベルなGUIではメッセージループと呼ばれる「OSから来たメッセージを処理する」という方式のプログラム方式になることもあります。
プログラミング初心者は、「main関数」と「イベント駆動」を覚えておけば、何でも簡単にOSの機能を使ってプログラミングを行うことができます。
ある意味、GUICUIの違いは、CUIが「必ずmain関数から同じように実行される(ただしコマンドラインオプションを引数に取る)」のに対して、GUIは「イベントごとにさまざまなコールバック関数を実行する」という違いになります。それだけで、基本的には、関数のコードブロックが実行される、ということに変わりはありません。

ウィンドウを表示しているのはWindowsのウィンドウ表示機構

また、GUIのプログラムに言えるのが、「ウィンドウを表示しているのは、全部Windowsのウィンドウ表示機構がやっている」ということです。
要するに、独自のプログラムはウィンドウに対する操作や命令にすぎず、全体の「ウィンドウ表示システム」がウィンドウを管轄して表示しているのです。
X Window Systemでもそれは同じです。全てのウィンドウ操作は、ウィンドウシステムが支配し、管轄し、管理し、全ての操作・表示機構をOSのウィンドウシステムが担っています。これを取りかえることはできません。そのため、WindowsではWindowsの、XではXのやり方にそってプログラムを開発する必要があります。プログラムというよりは、「ウィンドウシステムへのコマンド命令」だと思うと良いでしょう。そのコマンドを解するのは、GUIではWindowsやXであり、CUIではシェルやlibcやカーネルシステムコールなのです。
言ってしまえば、「こっちにはボタンとかフォームのコンポーネントがあるから、言ってくれたらこっちで組み立てるぜい」といった親方の仕事に近いと思います。もちろん自分でコントロールを作ることは可能で、多くのGUIプログラミングはこうしたコンポーネントの作成(継承とオーバーライドを使う)に多くが割かれます。
ある意味、設計としてはXの方がWindowsよりも優れているでしょう。それは、Xサーバーという「グラフィック全てを統括するサーバー」があるため、仕事が分かりやすいからです。全てのXプログラムは、Xサーバーに対してXプロトコルで通信する「通信相手」にすぎません。全てを表示しているのは、全てXサーバーなのです。
後日注記:決してGUIが劣っているわけではありません。WindowsではGUIがOSに統合されており、システムのAPIを呼び出すことでGUIアプリケーションの開発ができます。これはファイルシステムやソケットのシステムコールと変わりません。ですが、多機能・高機能すぎてプログラマ志望の入門者にとっては理解しづらい仕組みになっています。たとえば、Windowsではグラフィック操作を行うためにOSとデバイスの仲介役であるデバイスコンテキストを使います。こうした点が、Windowsプログラミングに挫折する大きな障壁となっています。

C

C言語はそもそもUNIXというOSを書くために生まれた言語。当初からアセンブラを高い移植性でもっと綺麗に書くことを目標としているため、「綺麗なアセンブラ」のような言語である、という特徴がある。ポインタのようなアセンブラ由来の仕組みを採用し、メモリ管理は自分で行わなければならない。アセンブラ言語についてはアセンブラを参照のこと。
そもそもがUNIXの言語であったため、UNIXというOSととても親和性が良い。UNIXAPIであるopen/close/read/writeは、C言語のライブラリAPIにもそのまま考え方として受け継がれている。LinuxAPIを参照のこと。
また、Linuxではgccのように、C言語コンパイラが無料かつオープンソースで用意されている、という点も挙げられる。Linuxでの開発は主にgccとmakeによって行う。Linuxディストリビューションを開発向けに導入するならば、こうした開発環境は必須である。Visual StudioWindows向けに買うよりもコストが安く、入門者向けである。Linuxはソフトウェアのソースコードもオープンであるため、tarballを解凍してすぐに改造することができる。GNUツールチェインやC/C++ツールを参照のこと。
だが、素のC言語では、コマンドラインでの手続き型プログラムの開発はできるが、オブジェクト指向のような「高度な機能」は用意されていない。この点は、C++の機能を使う必要がある。また、GUIの開発を行うためには、Xサーバーやそれなりのライブラリを使用しなければならない。X11GNOMEKDEを参照のこと。

Cの特徴

自力でメモリを管理しなければならない

C/C++言語は、Javaのようなガベージコレクション機能がないため、自分でメモリを管理しなければならない。
そのため、malloc()やfree()のようなメモリ管理関数を使って、動的に管理する場合は、取得したメモリを自分で解放しなければならない。
これは、OSのような基盤的プログラムでは、自分でメモリ管理が出来る、と言う意味で良い点だが、アプリケーションとなると、メモリリークの原因になってしまう。
そのため、Javaのような最初からガベージコレクションが備わっている言語の方が、アプリケーションの開発の意味では作りやすいだろう。
後日注記:逆に、手動で確保したメモリを解放するC/C++の方が、自分でやる代わり確実である、といった考え方もできる。Javaでは、参照がなくなったメモリ領域は自動で回収されるが、参照がある間メモリに残り続ける。C/C++では、自分の解放したい場合や状況に応じて、いつでもメモリを解放できる。

プラットフォーム依存性が高い

C/C++言語は、プラットフォーム依存性が高く、Windowsの環境ではWindows向けにしか開発出来ず、Linuxの環境ではLinux向けにしか開発出来ないところがある。
この点でも、Javaのようなクロスプラットフォーム性が最初から意識されている言語よりも、作りにくいところがあるかもしれない。

コンパイルしなければならない

また、PythonRubyのようなスクリプト言語から言うと、コンパイルしなければならない、という問題もある。
特に、サーバーサイドで動く言語の場合、サーバーに対して実行ファイルをコピーするのは難しい。サーバーで動く環境向けにコンパイルするのは出来ないこともある。
PerlPHPのようなスクリプト言語なら、サーバーにコピーするのはテキスト形式のスクリプトでよく、手軽である。
それから、Windows向けに配布する場合など、商用のコンパイラしかないことが考えられる。開発するために、有料でお金を出してコンパイラを買わなければならない。
スクリプト言語なら、Windowsの場合はインタプリタをインストールしなければならないかもしれないが、UNIXMacなどではスクリプトで簡単にコマンド一つで実行出来る。

ライブラリに頼らず、自分で全部作る必要がある

もちろんC/C++言語でもライブラリを使うことは出来るが、Javaや.NETのような最初から何でも揃っているライブラリがなかなか存在しない。
元々、C/C++のプログラミングでは、ライブラリのような関数も含めて、全て自分たちで開発しなければならない、という風潮がある。
もちろんC/C++の標準ライブラリも存在するが、たまに旧式の使ってはならない関数があったり、文字列をどうするのだと言った問題があったり、簡単に正規表現やネットワーク接続が出来ない、などの問題もある。
Javaや.NET、スクリプト言語などでは、そうしたほとんどの良く使われる関数は最初からクラスライブラリとして揃っている。Javaを使う理由は、まさにそれが一番多いだろう。

Cが動く環境は多く、システムに低レベルレイヤーは絶対に必要

C言語が動く環境は多い。そのため、機能の少ない組み込み系の開発には良く使われる。
それに、JavaRubyはあくまでお遊びのプログラミング言語だ。なぜなら、コンピュータは機械語で動くから、C言語で書かれた何らかのプログラムが既に存在しなければ、JavaRubyだって動かない。JavaRubyでプログラミングしたくても、C言語仮想マシンインタプリタを作らなければ、動くはずはない。

スピードが速い

C/C++言語はコンパイル言語であるため、Javaや.NETに比べて速度が速い。ValaやRustもコンパイル言語だが、C/C++より高速にはならない。

機能が多い

特に、最新のC++には機能が多い。
特に、スマートポインタのように、newとdeleteを使ってメモリを確保するだけではなく、安全かつ自動的にメモリを解放してくれる仕組みは最近存在する。
テンプレートやジェネリックのような機能もC++にはある。
だから、最新のC++を持ってすれば、Javaを使うべき領域は少なくなるかもしれない。

OS環境を書くための言語

C/C++は、「OSを書くための言語」であると言えます。ここで言うOSとは、カーネルUNIXのユーザーランドだけではなく、Xサーバーやデスクトップ環境、デスクトップアプリケーションまでを含めたOSです。
確かに、JavaRubyでアプリケーションを開発した方が、生産性も高くきちんと動くでしょう。ですが、たとえばOfficeやWebブラウザのようなアプリケーションを作りたい場合、C/C++を使って作ります。
WebブラウザGUIアプリケーションまで含めた「オペレーティング環境」を作るための言語がC/C++です。そのため、既存のシステムを改良したり、オープンソースで開発に参加したい全てのプログラマが、低レベルなC/C++を習得する必要があります。

機械でも分かるような手順を書く

C言語のプログラムは、その奇妙さから「何が書いてあるのか分からない」と言う人が多いです。
ですが、基本的にプログラムというものは、「機械にも分かるような手順を書く」だけです。
機械にでも分かる、というのは、機械には計算とメモリ操作しか分かりません。計算とレジスタやメモリの操作はアセンブリ言語で表現できますが、Cではそうしたものを「拡張」して、「構造化プログラミング」を行います。Cで書かれたプログラムは、原則アセンブリ言語コンパイルされ、機械語に変換された状態でCPUが読み取って実行されます。

Cにおけるプログラミング

C言語でプログラムを書くということは、計算とメモリ入出力しかできないCPUプロセッサに対して、「制限された中で機械でも分かるように手順を書く」ということです。
そう、Cのコードは手順にすぎません。それも、制約がかかった上での手順です。ですから、より分かりづらくなります。
このC言語の入門を読むことで、そのコードを「どのように解読すれば良いか」が分かります。
ですが、実際のところ、プログラミングで重要なのは、「記法」ではなく「目的」です。
プログラムは、人間の頭脳がやっていることと同じことをします。ある特定の領域に記憶領域を確保し、その中に記憶を整理しながら、計算し、論理に基づいて判断し、ファイルの読み書きやネットワークの転送やグラフィックスの描画を行います。
ここで、重要なのは、「人間の頭脳と同じことをできる」ということです。特に、プログラムは「自動的」に人間の頭脳と同じことをやります。「やっていることは人間のやっていることと全く同じ」なのです。
そして、大切なのは、記法ではなく、目的です。そのプログラムが、コンピュータの資源(リソース)を使って、どのようなことをやるか、という「明確な目的」が必要です。
プログラミングを学ぶ、ということは、決してプログラムを作るための記法を学ぶことだけではありません。プログラムの目的を明確化し、何をどのように行うかを考えながら、必要なリソースの処理と制御フローのアルゴリズムを考え、記法に落とし込んでいく、つまり「プログラミングとは、目的を明確化した上で、どのような手段で実現するかを考える」ということなのです。このために、C/C++Javaのようなプログラミング言語があるのです。

Cのいろいろ

ポインタと変数のアドレス

普通、変数は宣言されるごとに、新しい領域に変数のデータが確保されます。
x, y, zを順に次々に宣言すると、xとyとzはそれぞれ、別々の領域に変数が作られます。
これは、それぞれの場所に値を確保したいような、「値型の変数」では問題ありません。
ですが、「参照型の変数」とも呼ばれますが、ポインタを使うことで、「同じ場所(アドレス)にある別の名前の変数」を参照できます。
これは、たとえばデータ構造のように、「プログラムの中で、別の名前でも同じアドレスの変数を参照したい」時に使えます。特に、リストのようなデータ構造では、「リストに連結された次のデータ」を参照するために、ポインタを使います。
ポインタを応用することで、同じアドレスを関数の内部(呼び出し先)と外部(呼び出し元)で共有することもできます。こうした「データの共有」という考え方が、ポインタによって行えます。
つまり、「変数のアドレス」という考え方で、同じ場所にあるデータを「共有」し、「指し示す」という考え方が、ポインタです。
先の連結リストの例のように、ポインタを上手く使うことで、「データ構造の中に別の変数のアドレスを含める」といったようなことができます。ある場所にある変数を、別の場所から操作できるのです。
こうしたことができるのは、変数が単なる数値ではなく、「メモリアドレス上のデータ領域」であるためです。C言語では変数にその変数が位置するメモリアドレスでアクセスすることができます。

複数の名札から変数の入った箱そのものにアクセスする

たとえば、C言語

int x = 10;

とするのを、「int型の変数xに整数値10を代入する」と表現しますが、この時、変数とは「名札」とか「値を入れる箱」などと言った説明がされると思います。
先の例で言えば、xという「名札」のついた「箱」に、10という「整数値」を入れます。
この時、なんとなく、「名札=箱」だと思っていると思いますが、ポインタを使うと、ひとつの「箱」に対して複数の「名札」を付けることができます。
たとえば、

int *y = &x;
*y = 20;
printf("%d", x); /* 20 */

とすると、int型変数へのポインタyは、xと「同じ箱」を指しながら、「別の名前」を使っています。そのため、yの参照先を変えると、xの参照先も変わるのです。
プログラミングにおけるポインタを理解するためには、このように「名札と箱は必ずしもイコールではない」と考える必要があります。
なぜポインタを使うのか、という答えは、ポインタでしか作れないデータ構造や、ポインタを使った方が作りやすい関数などがあるためです。特に、連結リストを作ったり、あるデータ構造体の一部として別の変数を参照したい場合、あるいはカーネルのファイルバッファにアクセスしたい場合など、さまざまな低レベルな部分でポインタを使います。
そう、ポインタとは「名札ごとに箱を作るのではなく、たくさんの名札で箱を共有する(値の共有ではない)」ということです。そして、この箱がメモリアドレスなのです。

メモリアドレス空間

Linuxでは、プログラムがメモリ上にロードされると、カーネルがメモリアドレス空間にテキスト領域(プログラムコードを格納する)、ヒープ領域、スタック領域を各プロセスごとに割り当てる。
このうち、ヒープ領域とスタック領域がプログラムのデータの格納のために使われる。
機械語論理アドレスは、カーネルによって仲介され、メモリ上の物理アドレスへと翻訳される。それぞれに割り当てられたメモリは「ページ」という小さな単位でカーネルによってメモリ上に領域を割り当てられる。

簡単なプログラム

簡単な計算プログラム

Perlのページに公開したプログラムのC版です。

#include <stdio.h>

int main(void) {
  int x;
  int y;
  int i;
  
  x = 0;
  y = 0;
  
  for (i = 1; i <= 100; i++) {
    x += 10;
    y += i;
    printf("%d\t%d\t%d\n", x, y, i);
  }
}

お札の枚数を数えるプログラム

自分で作った指定された値段からお札の枚数を数えるプログラム。こういう小さなプログラムを書くのは楽しいです。みなさんにもプログラムの入門として、このようなプログラムを書いてみてはいかがでしょうか。

#include <stdio.h>
int main(void){
    int nedan = 98760;
    int x = nedan;
    int ichiman = 0;
    int gosen = 0;
    int sen = 0;
    int gohyaku = 0;
    int hyaku = 0;
    int amari = 0;
    while (x >= 10000) {
        x -= 10000;
        ichiman++;
    }
    while (x >= 5000) {
        x -= 5000;
        gosen++;
    }
    while (x >= 1000) {
        x -= 1000;
        sen++;
    }
    while (x >= 500) {
        x -= 500;
        gohyaku++;
    }
    while (x >= 100) {
        x -= 100;
        hyaku++;
    }
    amari = x;
    printf("%d円は一万円札%d枚、五千円札%d枚、千円札%d枚、五百円玉%dつ、百円玉%dつ、あまり%d円です。", nedan, ichiman, gosen, sen, gohyaku, hyaku, amari);
}

オンラインでソースコードコンパイル・実行できるPaiza.ioで開発しました。

ロボット

以下は自分の作ったロボットのコード。
Qiitaで、@shiracamusという方と共同で作りました。

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}

struct brain {
  int (*check)(struct _obj *obj);
  void (*action)(struct _obj *obj);
} brain[] = {
  { check1, action1 },
  { check2, action2 },
  { NULL, NULL }
};

void doing (struct _obj *obj) {
  struct brain *b;
  for (b = brain; b->check; b++) {
    if (b->check(obj)){
      b->action(obj);
    }
  }
}

int main (void) {
  struct _obj obj = { .p = 0 };
  struct _obj obj2 = { .p = 1 };
  while(1){
    doing(&obj);
    doing(&obj2);
  }
}

C++

(一部の内容でエキスパートCプログラミング―知られざるCの深層 (Ascii books)を参考に執筆しました。多くは自分で書きました。)
クラスは、C++の主要な機能で、ユーザーが独自に作ることのできるユーザー定義の型を宣言できる。
クラスでは、データ型を含む構造体と、その構造体を操作するメソッドを一体化することができる。
C言語では、構造体を操作する関数は作ることができるが、その構造体を間違った方法でアクセスする関数から、構造体のデータを守る術がない。
C++では、データ型の構造体とその構造体を操作するメソッドを一体化し、アクセス指定子を宣言することで、限られた範囲のメソッドからのみデータ構造を操作することができる。このような考え方をカプセル化と呼ぶ。
アクセス指定子には、public(公開)、private(非公開)、protected(派生クラスのみに公開)がある。これ以外にも、関連するキーワードとしてfriendとvirtualがある。friendを使うことで、一部のクラスや関数にだけ特別にデータにアクセスすることができるようにすることができる。これをfriendクラス、friend関数と呼ぶ。
C++で自分で作ったクラスは、構造体のように静的に宣言して、ローカルスコープで使う場合は、通常の型と同じように宣言し、メソッドはobj.meth()のように呼び出す。malloc()やfree()のように動的にメモリに確保する場合は、new/delete演算子を使って、型宣言に*をつけ、ClassA *obj = new ClassA();とする。この時、メソッドの実行はアロー演算子を用いてobj->meth()のように呼び出す。
クラスと同名のメソッドはコンストラクタと呼ばれ、クラスが最初に作られた時に初期化メソッドとして実行される。クラスと同名に~をつけたメソッドはデストラクタと呼ばれ、クラスが破棄された時に後処理を行うメソッドとして実行される。

class ClassA {
private:
    int test;
public:
    ClassA(int x) {
        test = x;
    }
    void testAssign(int x) {
        test = x;
        printf("%d\n", test);
    }
    void testIncr() {
        test++;
        printf("%d\n", test);
    }
    int testGet() {
        return test;
    }
}

void main(){
    ClassA *obj = new ClassA(10);
    obj->testIncr();
    obj->testAssign(100);
    obj->testIncr();
    obj->testIncr();
    delete obj;
}

クラス内の変数はメンバ変数あるいはフィールド変数、関数はメンバ関数あるいはメソッドと呼ばれる。クラスを元に作られた「生成された変数」はインスタンス変数と呼ばれる。
クラスは、継承を行うことで、既存のクラスに対して新しいデータ型やメソッドを追加し、publicやprotectedな変数を操作したり、さまざまな機能を追加することができる。
継承は以下のように行う。

class ClassB : public ClassA {
public:
    void testIncr10() {
        for (int i = 0; i < 10; i++) {
            testIncr();
        }
    }
}

クラスを継承した際、同名のメソッドを定義することで、元のクラスのメソッドを「上書き」し、書き換えることができる。これをオーバーライドと呼ぶ。オーバーライドを使うことで、クラスの一部分だけを独自に自分で実装し、処理内容を書き換えることができる。
また、C++ではオーバーロードを使うことで、同じ名前のメソッドを別の引数の型で使いまわしたり場合によって別のメソッドが実行されるようにできる。
C++では演算子オーバーロードすることで、+, =, *, -, [], ()などの演算子オーバーロードできる。

int ClassA::operator+(ClassA &ca) {
    return test + ca.testGet();
}

C++ではiostereamで演算子<<と>>を用いて入出力を行うが、これは演算子オーバーロードして実現されている。
また、virtualキーワードを使うことで、継承したメソッドを仮想関数にできる。仮想関数を用いると、たとえばClassAのメソッドを実行するのか、それともClassAを継承したClassBの同名のメソッドを実行するのかを「動的にバインディング」できる。仮想関数を使うには、基本クラスのメソッドにvirtual宣言をつけた上で、派生クラスでそれをオーバーライドする。このような考え方をポリモーフィズムと言う。
例外は、エラー処理の専用の制御構造で、どこかの時点でエラーができた時に、「例外を投げ」、それを「キャッチ」することで、エラー処理を分かりやすく単純にできる。
テンプレートは、さまざまな型を汎用的に使うことができるように、型の中の型をパラメータ化する。

template <class T> T func (T a, T b) {
    return a + b;
}

インライン関数は、特定の関数を呼び出しではなくマクロのように、命令コード中にインライン展開することができる。
参照渡しは、ポインタのように変数を参照型として扱える機能。Cにはポインタがあるだけで参照渡しをすることができなかった。C++では参照渡しの機能を言語仕様に取り入れた。
C++のライブラリはSTLを中核としている。STLではコンテナ(シーケンスの保持)、反復子(シーケンスの反復)、アルゴリズム(シーケンスの操作)、関数オブジェクトなどの機能を提供している。

C++の特徴

プログラマの登竜門

C++言語を習得することは、一流のプログラマにとっては必ず必要です。C++はあらゆる場面で使われる、プログラミング言語の「王道」と言っても良いでしょう。あなたがプログラミングのことを「本当に習得したい」と思っているのであれば、C++を習得しましょう。
ただし、最近は必ずしもC++で書く場面は少なくなってきています。Java, VB, C#のような高水準のオブジェクト指向言語や、Python, PHP, Rubyのようなお手軽なスクリプト言語を使うことも多いです。C++を習得してからそれらに移行すれば、Webやサーバー、それからWindowsのようなプラットフォームにも十分に対応できます。
ですが、C++には挫折者がとても多いです。それは、「ポインタ、クラス、テンプレート、STL、わけのわからない難しいものが多すぎる」からです。難しいと思うなら、PerlDelphiのような「コンピュータとしての方法や構造設計が分かりやすい言語」を試してみてください。それらを経験してからC++に戻ってくれば、きっと理解や習得が容易になるでしょう。

C/C++はプログラミング界の帝王

まさに、C/C++は、プログラミング界の帝王と言えます。それは、LinuxWindowsの多くのコンポーネントは、C/C++で書かれており、多くのネイティブアプリケーションもC/C++で書かれているからです。
JavaPythonのような他のプログラミング言語も、言語処理系そのものはC/C++で書かれています。
そう、C/C++で書かれている入手可能なソフトウェアリソースは、おそらく90%以上ではないかと思います。
よって、LinuxC/C++を学びましょう。Linuxを使う理由は、gccLLVM/ClangなどのC/C++コンパイラが無料かつオープンソースで手に入るからです。Linuxは多くのアプリケーションがオープンソースですが、この中でも、C/C++が使われているソフトウェアが多勢を占めています。
言ってしまえば、LinuxC/C++以外、学ぶ必要はありません。
ただし、ひとつ注意しておくべきことは、「ポインタでメモリ管理をきちんとするのは難しい」ということです。このことだけに注意すれば、むしろ、JavaPythonのような他の言語が「お遊び言語」に過ぎないことが良く分かるでしょう。
後日注記:基本的に、WindowsLinuxのような「OSやネイティブアプリケーション」を書く場合はC/C++を使い、「大規模なプログラミングやシステム構築」を行う場合はJavaやその他の言語を使えば良いでしょう。

ポインタとクラスの両立が難解でメモリ管理は手動

C++に言えることとして、「ポインタとクラスの両立は難解」であるということが言えます。
C++では、ポインタを使いながらクラスベースのオブジェクト指向を行います。これにより、柔軟性が高く、どんな場合のコードにも対応できるという利点がありますが、難解で何をやりたいのかコードを見ただけでは全く分からないという欠点もあります。
Javaとは違い、メモリ管理を自分でしなければなりません。ですが、基本的に「プログラムの中で確保したものは、必ずプログラムの中で廃棄する」ということをきちんとやれば良いのです。これは難しいことではありません。

システムプログラマの標準言語

C++システムプログラマ向けの言語で、システムの中にすでに存在するアプリケーションを改造したい場合、ほとんどはC/C++で開発を行います。
Javaの方が使いやすいとは言われますが、Javaは専門の巨大システムを構築するために使うのであって、Webブラウザを改良したいなどの「システムプログラミング」における用途では、C++を使わざるを得ません。
こうした分野では速度と性能が重視されるため、Javaで作ることは現実的ではありません。
また、このような分野で必要なのは「きちんと動くアプリケーション」であり、C++を使って「正しく動くコモディティ的(個性のない汎用製品的)なコード」を書かなければなりません。

基本的なコードが書けるようになったらEffective C++を読むこと

Effective C++は、C++の基本が分かった新米エンジニアが、「どのようなコードを書くべきか」ということを学ぶために有用です。
この本は、「C++のコードのデザイン原則」のような本で、可能な限りconstを使えとか、インターフェースは正しく使う時には使いやすく、間違った使い方では使いづらいようにしろとか、オブジェクトをコピーする時は全体をコピーしろとか、そうした「C++における細かな原則」が具体的で大まかに書かれた本です。
この本に目を通すことで、C++の基本的な「守るべき原則」が分かります。ですが、実際のC++のプログラミングができなければ分からないような内容も多く、初心者にはおすすめできません。初心者はもっと別のC++の入門本を買いましょう。
C/C++はさまざまな分野に用いられることが多く、柔軟な記述が可能であることを求められるため、実際のC++の現場についてどのようなコードを書いたらいいか検討がつかないような時に、この本にざっと目を通すと、「普通はこのように書くべきなのだ」ということが分かるでしょう。
Effective C++は時にとてもアルゴリズム的でコンピュータの内部に迫るようなことを言うために、数学のできない文系プログラマにとっては扱いづらい本かもしれませんが、きちんと同じことを考えると、「そうか、本当にこの方が効率が悪いのだな、そしてそれは私のせいなのだな」ということが分かって、とてもためになります。
後日注記:Effective C++C++カプセル化や効率などの面から見た「正しい選択」の分かる本だと思います。特に、コンストラクタが無駄に実行されるのを少なくするとか、カプセル化の面から見てクラスのメソッドでもfriendでもない関数を使うべきなのはなぜなのかなど、さまざまな「よく考えられた方法」を一気に習得できる本です。そのような観点から見ると、C++を使う上で参考にすべき設計の方法が見えてくると思います。

C++Smalltalk

レシーバ、メソッド、メッセージ

C++Smalltalkを参考にしたクラスベースのオブジェクト指向言語であり、Smalltalkの用語である「レシーバ」「メソッド」「メッセージ」は以下のような意味を持つ。

  • レシーバ
    • レシーバは直訳すれば「受け手」であり、メッセージを受け取る側のオブジェクトを表す。
  • メソッド
    • メソッドは、レシーバに対して働きかけるメッセージとその時の処理のことで、C++では関数として実装する。
  • メッセージ
    • レシーバのメソッドを実行することを、「レシーバにメッセージを与える」と言う。

たとえば、オブジェクトobjに対してメソッドmeth()を実行することを、「objにmethメッセージを与える」と表現する。
C++の「.」や「->」で言えば、.の左側にあるものをレシーバ、右側にあるものをメソッドとする。オブジェクト指向言語では、レシーバに対してメソッドを働きかけることで、あるデータに対する関数の「ふるまい」を記述して、プログラミングを行う。
C++オブジェクト指向のクラスやメソッドを使うことを「なんとなく理解する」ためには、こうしたSmalltalkの基礎知識が有用である。メソッドを実行する、ということが何を意味しているのか、分かると理解しやすい。

クラス階層

SmalltalkC++のようなオブジェクト指向言語では、オブジェクトを作成するために「クラス階層図」を利用する。
クラスとは、直訳すれば「階級」。
クラス階層図は、あらゆるデータ型は基本となるデータ型である「基本クラス」から派生したデータ型である「派生クラス」の、ひとつの抽象的な階層構造である「クラス階層」として表現される。全てのクラスは単一の「継承関係」にあり、基本のクラスを元にして派生クラスが作られる。
クラスには、オブジェクトの保持するデータの構造体メンバである「フィールド」あるいは「メンバ変数」と、そのメンバにはたらきかける付随の「メソッド」によって成り立つ。データ型とそれに付属するメソッドは、全体が継承関係にあり、基本のデータ型から派生したデータ型が作られ、クラス階層に記述されているメソッドからしか、データ型にはアクセスできない。
クラスベースのオブジェクト指向言語では、全てのオブジェクトの作成のために、こうした「クラス」を利用する。クラスはデータと関数をひとまとめにしたような構造体であると同時に、カプセル化や継承のような機能を提供する。自分でオリジナルのクラスを作るために、基本となるクラスを自分で継承することもできる。
クラスライブラリには以下のようなものがある。

MFC

MicrosoftによるWindows向けのクラスライブラリ。
標準のC++では提供されないさまざまなクラスや、
Windowsコントロールの高度なAPIを提供する。
また、ドキュメント・ビュー・アーキテクチャにより、
C++Windowsアプリケーションを実現するための、
ドキュメントとビューを中心とした高度なフレームワークを提供する。
言語はC++

Java

Javaのパッケージは、Java SE, Java EE, Java EEにおいて
それぞれのクラスライブラリを提供する。
言語はJava

.NET Framework

.NET FrameworkMicrosoft版のJavaのようなもので、
Windowsアプリケーションの開発のためのクラスライブラリを提供する。
言語はC#/VB.NETなど。

OPENSTEP

OPENSTEPはNeXTSTEP向けのオブジェクト指向開発環境。
macOSCocoaとして採用されているほか、
フリー版実装のオープンソースGNUstepなどがある。

GLib/GObject/GTK+/GNOME

GNOMEで採用されている、C言語によるオブジェクト指向を行うための機構。
GTK+GUIウィジェットを使用できる。
言語はC/C++/Python/JavaScript/Valaなど多数のバインディングが開発されている。
オープンソース

Qt/KDE Framework

KDEC++フレームワーク
QtやKDEと美しく統合されたアプリケーションを開発できる。
言語はC++のほかPythonなど。
オープンソース

オブジェクトに対する任意の演算子を定義する感覚に近い

C++におけるオブジェクト指向は、オブジェクトに対する任意の演算子を定義する感覚に近いです。
たとえば、数値なら、1+1=2の「+」のようにしたいところを、さまざまなオブジェクトに対して、「.add」や「.remove」のような形で定義するのです。
Smalltalkでは、こうしたメッセージ式を演算子と全く同じように定義して利用することができます。

C++オブジェクト指向入門

C++におけるクラス

クラスは同じ機能を持つオブジェクトを量産する仕組みで、メンバ変数とメンバ関数の設計図を書いてしまえば、あとはそれを使う側が宣言して呼び出すだけで、ひとまとめにした機能とデータを処理することができる。基本的に、C言語で言う構造体の中にそのデータを操作する関数を詰め込んだものだと考えれば良い。
クラスの中のメソッドの宣言とメソッドの実装を分離するには、C言語のようにメソッドをクラス内で宣言した上で、分離した実装部のクラス名とメソッド名の間に「::」をつける。
基本的に、各メソッドからインスタンス内のメンバ変数は「共有」されると考えると分かりやすい。どのメソッドからも同じメンバ変数を参照する。そして、インスタンス作成の時にコンストラクタによって初期化され、各メンバ変数はメモリを割り当てられる。
時に、動的に割り当てたい時もある。その時はnew/delete演算子を使う。C++では、メモリをnewで動的確保した場合は、使わなくなった時にdeleteで削除しなければならない。

C++のクラスには、データ、メソッド、インスタンスの作成の3つの記述部がある

C++のクラスについて言えることは、データの記述部、メソッドの記述部、そしてオブジェクトのインスタンスの作成とメソッドの呼び出しの記述部、という、3つの記述部があると言うことです。
たとえば、データの記述部は以下のようになります。

class Hoge
{
private:
    char name[256];
    int kazu;
public:
    void show(int x, int y);
    void setName(char* a_name);
};

次に、メソッドの記述部は以下のようになります。

void Hoge::show(int x, int y) {
    this->kazu = x + y;
    cout << this->name << ":" << this->kazu << endl;
}

void Hoge::setName(char* a_name)
{
    this->name = a_name;
}

そして、インスタンスの作成とメソッドの呼び出しは以下のようになります。

int main() {
    Hoge* obj = new Hoge();
    
    obj->setName("hogehoge");
    obj->show(3, 4);
    
    delete obj;
}

このように、C++によるオブジェクト指向は、データ、メソッド、そしてインスタンスの作成と呼び出しで成立します。このようにすることで、データをどの場所からでも操作でき、全てのメンバのメソッドから同じデータを参照でき、またインスタンスをいっぺんに動的に作成して、同じオブジェクトをポインタから参照できます。
C++では、obj->show()のようにオブジェクトのメソッドを実行しますが、この時objがshow()を参照しているように見えて、逆にshow()がobjを参照している、ということは注意が必要です。関数の方がポインタの先にあるデータを参照しています。
このように、データには「オブジェクトの状態」が保管され、メソッドでは「オブジェクトに対する変更と取得」が行われます。そして、実際にインスタンスを作る場所で、「オブジェクトの作成」が行われます。
(ここでオブジェクトの操作にポインタを用いたのは、ポインタによる操作の方がC++では一般的だからです。多くのコードで、「.」よりも「->」が使われます。また、thisポインタを使っているのは説明のためで、必ずしも使う必要はありません。普通に名前で参照できます。)
後日注記:こうしたC++の書き方を理解するためには、上述した「レシーバとメッセージ」の関係を理解することが有用です。あるレシーバオブジェクトに対して、メッセージを与え、オブジェクトのふるまいとして操作や処理を行うのです。

クラスと実装の隠蔽

クラス。
C++では、クラスとして実装の詳細を隠蔽することで、外部からアクセス不可能にし、クラス内部のメソッドの中だけでデータを共有して、インスタンス作成の時のインスタンス変数にそのデータを包括し、オブジェクトとして生成することができる。
これにより、データのアクセスは公開インターフェース用のメソッドを通じてしかできなくなる。
また、インスタンスはまるで「もの」であるかのように、必要なメンバ変数をメモリ上にいっぺんに割り当て、メソッドを通じてその「ものの属性」をさまざまに操作・変更することができる。
この時、変数の初期化をしたい場合はコンストラクタと呼ばれるクラス名と同じメソッドを実行する。
後始末はデストラクタで行う。デストラクタにはクラス名に~(チルダ)をつける。

アクセス属性

以下のアクセス属性を使うことで、クラスをカプセル化出来る。これにより、データへのアクセスが制限され、実装の詳細は外部から隠蔽できる。

アクセス属性 説明
private 不可視(外部に非公開)
protected 派生クラスを除き不可視(派生クラスを除き非公開)
public 可視(外部に公開)

コンストラクタとデストラク

コンストラクタは初期化、デストラクタは後始末のために使う。
コンストラクタはクラス名と同じメソッド名として作る。デストラクタは~Myclass()のように「~」をつける。

継承

継承を行うことで、クラスに別の機能を「追加」することができる。これを「差分プログラミング」という。
Windowsコントロールなどでは、継承を行うことで元にあったコントロールに機能を追加できる。たとえばテキストボックスにシンタックスハイライトの機能をつけたりすることが考えられる。

オーバーライド

クラスの継承を行った際、親クラスに含まれるメソッドの名前と同じ名前のメソッドを、そのクラスを継承した派生クラスのメソッドとして宣言することで、メソッドを上書き(オーバーライド)できる。
この時、親クラスのメソッドを呼ぶタイミングで、派生クラスのメソッドを呼ぶことができる。このため、「イベントに応じて適切なメソッドを書き換える」ことで、クラスのふるまいや動き方の中で必要な部分だけを書き換えることができる。「OnDrawメソッドが呼ばれる時だけ違うことをさせたい」時に利用できる。MFCなど多くのGUIフレームワークがこの特徴を使ってオーバーライドすることを前提にOSのAPIをラッピングしたクラスライブラリを提供している。
C++にsuperは無いが、スコープ解決演算子「::」を使えば、基底クラスのメソッドを派生クラスから呼ぶことができる。
また、C++11ではoverrideキーワードとfinalキーワードが追加された。overrideキーワードを使うことで、メソッドがオーバーライドされていることを明示的に示すことができ、正しくオーバーライドされていなければエラーを出してくれる。また、finalを指定したメソッドはオーバーライドできない。

friend関数・friendクラス

friend関数・friendクラスを使うことで、そのクラスのprivate/protectedメンバへのアクセスを他のクラスまたは関数に許可できる。「友達だけには教えてあげる」という感じ。

ポリモーフィズムとは

ポリモーフィズムとは多態性・多様性のことで、さまざまな場合や状況に応じてオブジェクトの異なる種類の型や異なる関数を共通の呼び出し方法で呼び出す仕組み。
たとえば、同じ名前のメソッドであっても、呼び出し元の引数の与え方によって別のメソッドを実行したり(多重定義・オーバーロード)、オブジェクトが基本クラスの型であるか派生クラスの型であるか、実行時にその時の状況に応じて判断し、動的に決定する仕組み(仮想関数)などがある。
ポリモーフィズムオブジェクト指向の真骨頂で、オブジェクト指向でどんなことができるのか、ここから見えてくる。

仮想関数

要するに、「鳥」というクラスから「ニワトリ」や「鳩」などのサブクラスを作ったとして、それぞれのサブクラスでオーバーライドして「鳴く」メソッドを作り、その鳥の種類と無関係にこの「鳴く」メソッドを実行する(実際の鳥の種類に応じて別々のメソッドを実行する)、つまり「動的バインディング」を行う、ということです。これが、仮想関数とvirtualによって可能になるのです。
仮想関数の例
基本クラスのメンバ関数を仮想関数と宣言した時、その呼び出しはコンパイル時ではなく実行時に動的に解決できる。
クラスのメンバ関数を仮想関数と宣言するためには、基本クラスでメンバ関数の前に「virtual」を指定する。
基本クラス定義で仮想関数宣言を行うと、派生クラスの定義ではvirtualキーワードがなくても自動的に仮想関数として扱われる。
以下は自分で適当に書いたコードです。
基本クラス(hello.h):

#include <iostream>
#include <string>
using namespace std;

class HelloBase {
public:
  virtual void hello() { cout << "Hello." << endl; }
}

派生クラス1(hello_german.h):

#include <iostream>
#include <string>
using namespace std;

class HelloGerman: public HelloBase {
public:
  void hello() { cout << "Guten Tag." << endl; }
}

派生クラス2(helllo_french.h):

#include <iostream>
#include <string>
using namespace std;

class HelloFrench: public HelloBase {
public:
  void hello() { cout << "Bonjour." << endl; }
}

main.cpp:

#include <iostream>
#include <string>
#include "hello.h"
#include "hello_german.h"
#include "hello_french.h"
using namespace std;

int main() {
  HelloBase *hl_en, *hl_de, *hl_fr;
  
  hl_en = new HelloBase();
  hl_de = new HelloGerman();
  hl_fr = new HelloFrench();
  
  hl_en->hello();
  hl_de->hello();
  hl_fr->hello();
  
  delete hl_en;
  delete hl_de;
  delete hl_fr;
  
  return 0;
}

実行結果:

Hello.
Guten Tag.
Bonjour.

親クラスのメソッドにvirtualキーワードをつけないと、こういうことはできない。この例の場合、virtualキーワードをつけないと、親クラス「HelloBase」のhelloメソッドが3回実行される。

動的なオブジェクトの作成

ポインタのようにスコープと無関係のオブジェクトを作成できる

C++では、動的なオブジェクトの作成(よくオブジェクトのポインタと呼ばれる)を使うことで、スコープと無関係に作ったり参照したりできるオブジェクトを作ることができる。
型を宣言する時は、ポインタと同じように「Class *obj」と「*」(アスタリスク)をつけて宣言する。ここに、new演算子を用いてコンストラクタを実行する。newで動的に作られたインスタンスは、deleteで削除するまで、スコープとは無関係に保持され続ける。そして、その変数の実体はポインタからどこからでも参照できる。
ただし、ポインタがnullを参照したままでオブジェクトのメソッドを実行しようとしたり、参照しているポインタがなくなってもオブジェクトが幽霊状態のまま削除もされずに残ってしまったり、場合によっては削除されずにメモリ領域をどんどん占領していってメモリリークの原因になったりすることもある。
Javaのような言語では、仮想マシンガーベッジコレクションを行うことで、参照されなくなったオブジェクトは自動的に仮想マシンによって削除される。C++でも、最近の仕様であるスマートポインタを使うことで、自動的に要らなくなったオブジェクトを回収できる。また、Rustのような先進的な言語では、ムーブセマンティクスと所有権・寿命の考え方によって、ポインタを使わなくても、ミュータブルな参照の限定(所有権はひとつだけで、参照はいくらでも作れるが、ミュータブルな参照はひとつだけ)を行った上でスタックにデータを置くことで、「メモリ管理問題の最終的な解決」を行っている。
C++の優位な点は、高速であること。Javaのような仮想マシンによる言語に比べて、自分でメモリを生成・削除の管理をしなくてはいけないが、その分だけ高速かつ効率的にアプリケーションを開発できる。また細かい点として、Javaには変数の型によって値型と参照型があり、オブジェクトや配列は参照型だが、int型などは値型である、などといった規定がある。C++では、ポインタあるいは参照型を使うことで、こうした値型と参照型を自分で明示的に指定できる。

動的なオブジェクトの作成とは

C++では、オブジェクトを動的に作成する(静的な変数として宣言せず、ポインタのような同じアドレスを参照する参照型の変数として、実行時に作り、スコープを超えても削除されない変数)を扱うことができる。
まず、オブジェクトのインスタンスのクラス名に*をつけて、

Tab *ptab;

のように宣言する。この上で、変数を動的に作成したい時に、

new Tab

を行い、これを先ほどのptabに代入して操作する。ここで、ptabがTabを参照するための「参照型変数」であり、C言語でいうアドレスだけを保管したポインタのようなものである。このインスタンスからメソッドを実行するために、->をつけて

ptab->Clear();

のようにメソッドを実行する。
動的な変数であり、ローカルスコープを超えてもインスタンスが残るため、ptabが「どこも参照していない状態」になっても、new Tabで作った「幽霊オブジェクト」は残り続ける。よって、オブジェクトは幽霊状態になる前に

delete ptab;

で消さなければならない。
Javaなどでは、値型と参照型の変数の種類があって、クラスは最初から参照型に決まっている。よって、ポインタのように*を付ける必要はない。また、Javaガーベッジコレクション機能があり、「どこからも参照されていないメモリ上のオブジェクトを自動的にゴミ掃除する」機能があるため、deleteも必要ない。その代り、速度が遅い。C++では自分でfreeやdeleteを行う代わり、ガーベッジコレクションに頼らないため、速度が桁違いに速い、という特徴がある。また、最近ではC++でもスマートポインタのような自動的に削除される賢いポインタの機能もある。

基本クラスに派生クラスのオブジェクトをつめこむ

また、オブジェクトをポインタで扱う用途として、基本クラスのオブジェクトのポインタに派生クラスのオブジェクトのインスタンスを格納したりすることができる。

HelloBase *hello_obj;

hello_obj = new HelloFrench();
hello_obj->hello();
delete hello_obj;

hello_obj = new HelloGerman();
hello_obj->hello();
delete hello_obj;

newとdelete

C言語malloc()/free()関数と同じことを、C++ではnew演算子とdelete演算子を使って行うことができる。
動的に作成する場合、C言語のポインタと同じように変数名(クラス名)に*をつける。動的に作成したオブジェクトからのメソッドは->をつけて実行する。

ジェネリック

テンプレート

テンプレートを使うことで、「どんな型にでも汎用的に対応できる型」を作ることができる。特定の型に依存しないアルゴリズムを書くことができるため、「int型でもchar型でもどちらでも使えるようなリスト型データ構造を作りたい」などといった場合に使う。
C++に標準ライブラリとして備わっているSTLでは、テンプレートを多用することでさまざまな型に対応してアルゴリズム・コンテナ・反復子などを使うことができる。

テンプレートは型を作る型

テンプレートは、一言で言えば型を作る型です。「どんな型でも一般的に使うことができる」という理由で、「Generics」(一般とか総称という意味)という名称で呼ばれます。
ジェネリックプログラミングは、C#Javaでも見られる考え方です。要するに、Listと一言に言っても、StringのListやIntegerのListがあります。こうしたものを、総称的に使うことができるのです。
これらは、動的スクリプト言語ではあまり言われない考え方です。それは、動的スクリプト言語では、そもそも型を動的に扱うため、リストやタプルの中でさまざまな型を「ミックスして」使うことができるからです。
テンプレートは最初はList<String> lst;などと書く必要があり、とまどうかもしれません。また、C++STLではこうしたジェネリックプログラミングの考え方を多用し、「テンプレートメタプログラミング」という全くありえない考え方で、スマートにさまざまなコンテナやアルゴリズムを操作することができます。全ては繋がっています。

テンプレートの基本

テンプレートとは、どんな型でも使用できる汎用的な型を含むクラスや関数のこと。
C++でのテンプレートでは、必要に応じて必要な型を指定し、クラスをある型のためにその都度作って使うというやり方ができる。
たとえば、汎用関数は以下のように定義する。

template <typename T> T Function1(T x)
{
    ...
}

このFunction1にint型の引数を与えると、int型の返り値を返す。
また、汎用クラスは以下のように定義する。

template <class T> class Class1 {
    T tval;
    
    ...
}

汎用クラスのオブジェクトは以下のように作成する。

Class1<int> obj;

ここでT tvalの型は、どんな型、たとえば数値や文字列やあらゆるクラスのオブジェクトに、宣言された時の型の情報に基づいて「変貌」する。数値型であっても、文字列型であっても成り立つような、何にでもなれるデータ型になる。
動的スクリプト言語との違いは、使う前に必ずClass1<int>のように事前に型を明示的に指定しなければならない。
Javaのようなオブジェクト指向言語では、Object型が全てのクラスの親クラスとなっており、Object型に全てのオブジェクトを詰め込むことができる。感覚としては近いかもしれないが、テンプレートは継承環境にない型やクラスであっても、どんな型でも成り立つ。
C++の標準ライブラリに含まれるSTL(Standard Template Library)では、どんな型でも出し入れすることのできるコンテナや、どんな型でも順番に処理することができるイテレータなどのシーケンスにテンプレートが使われている。

STL

STLの要素

STLは標準ライブラリとして含まれているC++のテンプレートを活用したライブラリ。何でもできる柔軟性と調和された美しさを持っている。
標準C++ライブラリの一部で、テンプレートを使った基本的なデータ構造を提供するライブラリ。
コンテナ、イテレータ(反復子)、アルゴリズム、関数オブジェクトの各要素から構成されている。

コンテナ

STLのコンテナは、「複数の値をひとまとめに格納する」ためのクラス。
STLのコンテナではテンプレートを使うことで、さまざまな値をひとつのコンテナに格納・取り出しできる。
STLのコンテナでは、テンプレートを使うことで、任意の型の複数の値を持ったコレクションを扱い、要素を格納・取り出しすることができる。

反復子

反復子を使うことで、コンテナのそれぞれの要素を順番に処理できる。

アルゴリズム

アルゴリズムを使うことで、コンテナのそれぞれの要素をソートしたり検索したり一致判断や結合などを行うことができる。

簡単な説明

アセンブラC/C++

ファイルディスクリプタ

ストリームを識別する指定子
プロセスには標準入力、標準出力、標準エラー出力の3つのファイルディスクリプタが自動的に設定される
これら以外に、ファイルをopenした場合やsocketを作った場合にもファイルディスクリプタが設定され、これらを用いてreadやwriteを行う

システムコールとstdio

システムコールのreadやwriteは、固定長入出力であり、「行単位」とか「文字・バイト単位」という入出力ができない。stdioではこうした便利な入出力関数が用意されている
テキスト処理でよく使うやり方として、4096など固定長のバッファを作っておいて、一時的にバッファにコピーしながら処理をしていくというやり方がある。少しずつ処理をするので可変長の文字バッファを得る必要がなく、一度に確保するよりもメモリ使用量が抑えられる。Pythonなどでは同様の目的にジェネレータを使うこともできる
getsは使ってはならない。確保したメモリ領域よりも大きな入力が行われた際に、バッファオーバーランを防ぐ手段がないからである。scanfも同様の理由で使うのはおすすめしない(scanfの場合は文字幅指定で回避できる)。同様に文字列をコピーするstrcpyも使用すべきでなく、strcpy_sを使うことが推奨される

stdioバッファ

stdioではバッファ処理を行うためシステムコール呼び出しよりも速度が速い

ファイル型のポインタ

ファイル型(FILE)のポインタはstdioでファイルを識別するのに使う
FILEの中にはファイルディスクリプタやバッファの情報が格納されている。要するにラッパー

fork, exec. exit, wait

forkすると、forkした親プロセスと子プロセスが2つに分かれ、どちらも同じ場所からその後の処理に進む
execすると、その時点でそのプロセスが上書きされ、新しいプロセスが実行される
基本的にforkしてexecすることで新しいプロセスを実行する

仮想関数

基本クラスで型付けされた変数に派生クラスのインスタンスを格納した場合、virtualキーワードを基本クラスのメソッドに付けることで、基本クラスのメソッドなのか派生クラスのメソッドなのか、動的バインディングでアクセスできる
たとえばHelloクラスのsayメソッドについて、Helloクラスを継承したHelloJapaneseクラスのsayメソッドを、動的にバインディングして呼び出したりできる
Hello.say()かHelloJapanese.say()かはインスタンスがどの型のクラスのインスタンスかによって動的に決まる