Help us understand the problem. What is going on with this article?

ハローワールド徹底解説

More than 1 year has passed since last update.

この記事は 初心者C++er Advent Calendar 2016 6日目の記事です。

"Hello, World!" とは

この記事は初心者向けの記事ではありますが、"Hello, World!" くらいは目にしたことのある方がほとんどだろう、と思います。
とはいえ聞いたことのない方の為に一応説明すると、この文字列はプログラミング言語の入門時に例として使われる文字列です。
どういう風に使うかというと、コマンドラインツールを作る場合はあの黒い画面に"Hello, World!"と出力したり、

"Hello, World!"とだけ書かれたウィンドウを作ってみたり、

"Hello, World!"と表示するダイアログを出してみたり、

ぶっちゃけ文字列の内容に意味はありません。
他にどんな物があるかを知りたければ、Wikipediaのこの記事とか見るといいかもしれません。
(2018年4月追記:上記の記事は削除されてしまっていました)

C++の"Hello, World!"

さて、多分に漏れず、C++にも典型的な"Hello, World!"プログラムがあります。
全くの初心者でなければ、見たことがある方がほとんどでしょう。以下のようなプログラムです。

#include <iostream>

int main() {
    using namespace std;

    cout << "Hello, World!" << endl;
    return 0;
}

細かい差異はあれど、大体こんなところでしょう。

よくある入門者向けの講座などで、こういう言葉を目にすることがあるかと思います。

「ここはおまじないだと思ってください」

便利な言葉ですね。おまじない。よくわからないかも知れないけど、とりあえずそういうことにしておけ、という乱暴な説明です。
別に、おまじないだからって、唱えればなんでも解決できる訳じゃないんです。
ちゃんと意味はあるのだけど、今はそこまで説明すると大変だから、後回しにするよ、ということを「おまじない」と言っているわけですね。
便利な言葉ではあるのですが、おまじないをおまじないのままで覚えてしまうと、後から応用が利かなくなってしまいます1

おまじないをおまじないのままにしておくのはやめましょう。
そんなわけで、この記事は「徹底解説」と銘打つことにしました。"Hello, World!"という入門中の入門プログラムを、頭から尻尾まで詳しく見ていきたいと思います。

念の為に申し添えておきますが、この文章には巷の入門講座を否定するつもりではありません。プログラミング言語はどれもある程度の複雑性を有していて、分かりやすくするためにある程度の省略を行うのは仕方がありません。
本当に厳密さを求めるなら、規格書を読めば良いという話になりますし、私もそこまで厳密な解説は書けないでしょう。

#include <iostream>

まずは一行目。これも「おまじない」とされることが多いです。
実際、この行はC++のプログラムの中ではやや異色な位置にあります。入門講座の執筆者にとっては、別のことから解説したくなることが多いのではないでしょうか。

でもそんなの関係無え。

なんたって徹底解説ですからね。これだってちゃんと解説しますよ。
この#includeを始め、#で始まる行には特別な意味があります。プリプロセッサー命令(Preprocessing-directives)です。

これを理解するためにはまず、C++の実装でソースコードがどのような過程を辿って実行ファイルに変換されるのかが分かっていないといけません。

C++のソースコードを実行ファイルに変換する過程を、大雑把に「コンパイル」と呼ぶことがあります。
しかし、実際には、C++のソースコードは複数回の処理を経て、ようやく実行ファイルに変換できます。
具体的には、

  • プリプロセス
  • コンパイル
  • アセンブル
  • リンク

などの処理があります2

実際、これら全ての過程は、特にオプションを指定しなければコンパイラがコマンド一つで自動的にやってくれます。ただ、内部的にはこれらの過程を経ているのです。

上記の過程の一つに、「プリプロセス」があります。プリプロセッサーはこのプリプロセスを行うプログラムを指し、プリプロセッサー命令は文字通りプリプロセッサーに対する命令です。
つまり、

#include <iostream>

という文は、プリプロセッサーに対して、「<iostream>includeしろ」という命令を書いている訳です。

さて、ではプリプロセッサーはどのような処理を行っているのかを説明します。
pre-processという言葉は、日本語で言うなら「前処理」でしょうか。あまり具体的じゃないですね。結局何を処理しているのかと。
プリプロセッサーがやっているのは、主に文字列処理です。プリプロセッサーにテキストを読みこませると、一定のルールに従って文字列を変換したテキストが出力されます。
一定のルールとは

  • マクロ定義された文字列が現れた場合、置き換えを行う
  • 連続する文字列リテラルがあった場合、結合を行う
  • #で始まる行がある場合、以下の処理を行う
    • #define: マクロを登録する
    • #undef: 登録されているマクロを削除する
    • #include: 解析中のファイルとの相対パス、もしくはインクルードパスからファイルを検索し、その行に展開する
    • #if, #ifdef, #ifndef, #elif, #else, #endif: 条件分岐を行う。条件が偽となった部分は出力されない。条件式はネストできる。
    • #line: 現在の行数を変更する。
    • #pragma: プリプロセッサーの実装ごとに固有の命令を書くことができる。この命令はプリプロセッサーで完結しない場合がある。
    • #error: 指定されたエラーメッセージを出力し、コンパイルエラーを起こす
    • それ以外: 不正なプリプロセッサー命令と判断し、コンパイルエラーを起こす(実装ごとの拡張による例外あり)
  • それ以外の場合はそのまま出力する

おおむね上記のようなものになります。
つまり、#include命令は、他のファイルの内容を読み込むために用意されています。
この時、読み込まれたファイルも連鎖的にプリプロセッサーで処理されます。

プリプロセッサーの出力のみを見たい場合、g++/clang++なら、

$ g++ -E -P ファイル名
$ clang++ -E -P ファイル名

Visual C++なら、

> cl.exe /EP ファイル名

とすることで、プリプロセス済みの文字列を得られます。

以下に例を示します。

a.cpp
a.cpp start
#include "b.hpp"
a.cpp end
b.hpp
b.hpp start
#include "c.hpp"
b.hpp end
c.hpp
c.hpp

プリプロセスのみを行うなら、C++の文法的に正しくないものでも構いません。
出力は次のようになります。

出力
a.cpp start
b.hpp start
c.hpp
b.hpp end
a.cpp end

"c.hpp"の中身が"b.hpp"内部の#include "c.hpp"の部分に置き換わり、さらに置換が行われた"b.hpp"の中身が"a.cpp"内部の#include "b.hpp"の部分に置き換わっているのが分かるでしょうか。

さて、ここで疑問に思っている方もいるかもしれません。例では#includeの後のファイル名は""で囲んでいますが、"Hello, World!"の中では#include <iostream>のように<>で囲んでいました。
#include命令には、""を使ってファイル名を指定する方法と、<>を使って指定する方法の2種類が用意されていて、それぞれ挙動が異なります。

#include "path/to/file"と書いた場合、プリプロセッサーはまず、処理中のファイルからの相対パスでファイルを探し、見つからなかった場合、事前に組み込まれた標準ライブラリのパスや、追加で渡されたインクルードパスを探します。
一方、#include <path/to/file>と書いた場合は、プリプロセッサーは相対パスから検索を行わず、インクルードパスの中だけを検索します。
インクルードパスは、g++/clang++なら-Iオプションで、MSVCなら/Iオプションで渡すことができます。

試しに、このようなディレクトリ構造でファイルを配置してみましょう。

/a.cpp
/b.hpp
/c.hpp
/include/b.hpp
/include/c.hpp

a.cpp
#include "b.hpp"
#include <b.hpp>
#include "c.hpp"
#include "d.hpp"
#include <d.hpp>
b.hpp
b.hpp
c.hpp
c.hpp
d.hpp
d.hpp
include/b.hpp
include/b.hpp
include/d.hpp
include/d.hpp
$ g++ -E -P -I/include a.cpp
出力
b.hpp
include/b.hpp
c.hpp
include/d.hpp
include/d.hpp

相対パスとインクルードパスの双方に存在する"b.hpp"というファイルは別々の物が読み込まれ、インクルードパスにしか存在しない"d.hpp"は同じものが読み込まれています。
ちなみに、相対パス上にしか存在しない"c.hpp"は、#include <c.hpp>と書くとエラーになります。

さあ、これで一行目の意味は分かったでしょう。
#include <iostream>は、インクルードパス(この場合は特に、事前に組み込まれた標準ライブラリのパス)から、iostreamという名前のファイルを読み込んで、その場所に展開するというプリプロセッサーの処理を書いたものでした。

ついでながら、他のプリプロセッサー命令についてもざっくりと説明します。

#define

プリプロセッサーマクロを定義します。プリプロセッサーマクロには、以下の2種類の形式があります

オブジェクト形式マクロ

#define MACRO_NAME some strings

この例では、ソースファイル中に出現するMACRO_NAMEという文字列が、some stringsという文字列に置き換えられます。
マクロ名は空白や記号(_を除く)で区切られた文字列が該当します。従って、ソースファイル中に例えばNOT_MACRO_NAMEという文字列が出現しても、NOT_some stringに置換されることはありません。

関数形式マクロ

#define MACRO_FUNCTION(x) My name is x.

この例では、ソースファイル中に、例えばMACRO_FUNCTION(kazatsuyu) という文字列が出現した時に、My name is kazatsuyu.に置き換えられます。
関数形式マクロの定義では、マクロ名と(の間に空白を入れてはいけません。空白を入れると、オブジェクト形式マクロと判断されます。

// "(x) My name is x."という文字列に置き換えられるオブジェクト形式マクロ"MACRO_FUNCTION"が定義されてしまう
#define MACRO_FUNCTION (x) My name is x.

また、関数形式マクロは複数の引数をとることもできます。その場合

#define MACRO_FUNCTION(x, y) My name is x, and I'm y years old.

などのように、カンマで区切ってください。
関数形式マクロを使うといろいろ複雑な処理ができるのですが、ここではこれ以上深追いはしません。

反プリプロセッサーマクロ組織の構成員より一言

と、ここまでマクロの説明をしましたが、マクロは基本的に使用してはいけません。いくつか理由があるので以下に示します。

  • グローバル空間にしか定義できない
    C++には名前空間という、別のライブラリと名前が衝突しないようにする機能がありますが、プリプロセッサーマクロはそういうことができません。特に、短い名前のマクロを定義してしまうと、非常に衝突の可能性が高くなります。その上、せっかく名前空間内で定義したC++の識別子と衝突することがあります3
    どうしても使用する場合は、固有のプレフィックスを付けて、なるべく衝突しない名前にしましょう。

  • コードが読みにくくなる
    マクロは非常に強力です。強力過ぎて、マクロを使えば何でも置き換えができてしまいます。C++の文法に合わないような記法も簡単にできてしまいます。

それこそ、


なんてこともできてしまいます(C#に見えるかも知れませんが、C++のコードです)。
もちろんこれはジョークですが、マクロを多用すると何でもできすぎてしまうのです。

※ネタばらし

マクロが必要になる箇所

ただ、どうしてもマクロを使わなければならない箇所もあります。一つはインクルードガードです。
インクルードガードとは、

#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD

// Some C++ codes

#endif

と書いておくことで、同じファイルが複数回インクルードされても、#ifndef#endifの間は一回しか展開されないようにできます。(#ifndef#endifに関しては次項参照)

ただし、現在主流の実装では、#pragma onceと書くことで、ファイル単位でインクルードガードと同等の効果があります。
問題は、#pragma onceは実装ごとの拡張機能であり、標準化されていないため、全ての実装で使えるとは限らない点です。

他にも、コンパイラ・OS・アーキテクチャの判定や、デバッグ時のみ有効なトレースポイントの挿入など、マクロを利用しないと実現できないこともあります。
デバッグで関数名・ファイル・行数などを取得したい場合も(今のところは)同様です。

マクロ定義オプション

プリプロセッサーに定義済みマクロを追加するオプションがあります。
g++/clang++なら-D、MSVCなら/Dを使います。
例えば、

$ g++ -Dfoo=bar a.cpp

とすると、事前に

#define foo bar

と定義されたように振る舞います

#undef

定義済みのマクロ定義を削除します。一時的に定義したマクロを削除する、気に食わない定義済みマクロを消すなどのことが可能です。

#undef min
#undef max

とか。
……やるときは、やっても大丈夫かどうか、ちゃんと検討してね?

#if, #ifdef, #ifndef, #elif, #else, #endif

条件分岐です。
#ifは、その後ろに現れる条件が偽なら、#else, #elif, #endifのいずれかが出るまでの間を出力しません。条件が真なら、出力を続けます。
条件が偽となった場合、出力されない部分の内容は全て無視されます。不正なプリプロセッサー命令などが存在しても無視されます。
#ifの条件式には、比較演算や論理演算を使用することができます。
また、definedキーワードを使って、マクロ定義の有無を調べることができます。

#if 1
出力される
#endif
#if 0
出力されない
#endif
#if true
出力される
#endif
#if false
出力されない
#endif
#if 1 > 0
出力される
#endif
#if 1 <= 0
出力されない
#endif
#if 1 || 0
出力される
#endif
#if 1 && 0
出力されない
#endif
#define a
#if defined(a)
出力される
#endif
#if defined(b)
オプションでマクロ定義されていない限り出力されない
#endif

#elif#if命令が偽だった時に実行される条件分岐です。条件式は#ifと同等です。
#elseは直前の#if命令や#elif命令が偽だった場合に真になる分岐です。
#endifは条件分岐の終了を示します。
#ifdef, #ifndefはそれぞれ、マクロが定義されているか、あるいは定義されていないか実行する条件分岐です。
#ifdef a#if defined(a), #ifndef a#if !defined(a)と同じ意味になります。

#line

この文章を書くにあたってプリプロセッサーを調べなおしていたら見つけました。今まで知りませんでした。

#line 10000

と書くと、次の行が10000行目ということになります。たとえそこが2行目であっても。
ちなみに、現在の行数は__LINE__と書くと得ることができるので、

#line 10000
__LINE__
#line 1
__LINE__

と書いてプリプロセッサーを実行すると、

出力
10000
1

となります。

行を変えたところで何か意味があるかというと、ほとんどの場合何の意味もない上に、デバッグがうまく行かなくなる可能性が高いのではないかと思います。つまり使う必要はないです。

#pragma

実装ごとに拡張命令を加えることができます。有名どころは、#pragma onceや、MSVCでの#pragma warnings, #pragma commentなどでしょうか。互換性が保証されていないので、使用する際はよく検討しましょう。

さて、だいぶ関係ない範囲も解説しましたが、これにて1行目は終わりです。まだ1行目です。なんか文字数数えたら9000字近いみたいですが、続きます。

int main() {

main関数です。main関数は、C++のプログラムの初期化が終わった後に、最初に呼び出される関数です。
なんて言うと、「WinMainは?」と思う方もいるかもしれませんが、あれはMSVC独自の規格です。

C++の国際規格では、プログラムの開始点はmain関数であり、それはint ()またはint (int, char**)という型であることが推奨されています4
とは言え、そもそも「関数って何? 型って何?」という方もいるでしょう。読者の方にはいないかもしれませんが、徹底解説と銘打った以上、そこから解説しなければなりません。

どのプログラミング言語にも、「関数」「型」「変数」「値」といった基本的な概念が存在します。
これが複雑に絡み合っていて、一つだけを解説する、ということがとても難しい。だから、「とりあえずこう書いておけ」という解説が多いのです。

mainは関数なので、まず関数の説明から入りましょう。
C++における"関数"とは、
「何らかの値を受け取って(受け取らないこともある)、何らかの処理をして(しないこともある)、何らかの値を返す(返さないこともある)もの」
です。
まって。怒らないで。だって本当にそうなんです。
と言っても、これでは全くもって何のことか分からないと思いますので、もうちょっと具体的に見てみましょう。

関数(function)という言葉は、もともと数学の関数から借りてきています。
数学的な関数だと、例えば

f\left(x\right)=x^2

とかそういうものが思い浮かぶでしょう。C++の(というよりプログラミング言語全般における)関数は、この関数の概念を拡張したものなので、逆に言えば、数学的な関数を(それなりに)表すことができます。
上記の関数の場合、

int f(int x) {
    return x * x;
}

と書けば、(intで表せる数値の範囲であれば)数学の関数と同じ結果が得られる訳です。

さっきから歯に物が挟まったような言い方をしているのには訳があります。
当然のことですが、コンピューターの使えるリソースには限界があって、どんな実装であれ、その限界を超えて表すことのできる数はありません。
例えば、グラハム数などを表わせともし言われてもまず不可能です。また、無理数を表すこともできません。無理数の場合は、どうしても近似値を扱うことになります。

さて、上記の関数fですが、int型の値を一つ受け取り、int型の値を返す関数です。
「型(type)」という用語を持ちだしたからには、型とは何か、について説明しなければなりません。

「型」というものを正確に説明するのは、なかなか骨の折れることだと私は思っています。
「型」という考え方は多分に抽象的なもので、これといった具体例を示すのが難しいからです。
だから、なるべく分かりやすく型の説明をしようとして具体例を出すと、車が走ったり犬が鳴いたりするのです。

犬や車で例えるのも分からなくはないのですが、具体例と言うのなら既にintは型であるという例があるので、これを解説しようかと思います。

intは整数を表す型です。もっと言うなら、一定範囲の整数を表す型です。整数は(可算)無限個存在しますが、無限個の整数を表すことのできる方法はありません。なので、コンピューターで表せる整数の範囲は一定の限界があります。
その限界がどの程度であるのかは、これはC++の規格では実装依存とされているので一概には言えないのですが、少なくとも、int型は整数以外の値を表すことはできません。
逆に言えば、ある値の型がintであれば、「その値は整数である」ということが保証されます。

C++では、全ての値には型があります。そして、型が異なれば、その値が表すものも異なります。
関数の引数にも型があるので、異なる型の値を受け取ることはできません5

改めて、

int f(int x) {
    return x * x;
}

という関数を見てみましょう。
関数fint型の値をxという名前で受け取ります。このx仮引数と呼びます。
そして、x * xという式を実行します。*は乗算を行う演算子なので6、つまりxの2乗が得られます。
returnというキーワードは、呼び出し元に結果を返す時に使います。返された値を戻り値と言います。
戻り値の型は、関数名の直前に書かれた型が表しています。つまり、この場合はintです。

C++では全ての値に型があります。関数も例外ではありません。関数の型は、戻り値の型 (引数の型)という表記をします。引数は複数個取ることができますが、その場合は,で区切ります。
関数fの型はint (int)である、ということになりますね。

上記の例のように、{}の中に関数が行う処理を書いたものを、関数の定義(definition)と言います。
一方で、定義を書かずに名前と型だけを書くこともあります。これを宣言(declaration)と言います。宣言を書くときは、

int f(int x);

と、このように{}を書かずに;を置いて終わらせます。
C++では、関数を呼び出すときは、事前に定義または宣言が書かれていないといけません。
関数の宣言は、関数プロトタイプ宣言と呼ばれることもあります。fのプロトタイプはint f(int x);である、といった使い方をされたりもします。

int g() {
    return f(2);
}

という関数を定義した時に、fが宣言も定義もされていなかったらコンパイルエラーになります。

int f(int x);

int g() {
    return f(2);
}

と書いてあれば、関数gに関してはエラーなくコンパイルできます。

宣言は「こういう関数があるよ」ということを知らせるために書くもので、定義は「この関数はこういう処理をするよ」ということを決めるために書くものです。
同じ関数に対する宣言は何度書いても良いですが、原則として定義は一回でなければなりません。これをODR(One Definition Rule)と言います7

さて、main関数の話に戻りましょう。

入出力であったり、画像処理であったり、通信であったり、およそC++のプログラムが行う処理のほとんどは関数の中に記述されます。一つの関数は、別の関数を内部で呼び出すことができます。
そして、C++のプログラムで最初に呼び出されるのがmain関数です。C++のプログラムのほとんどの処理は、main関数が始まってから終わるまでの間に実行されます8

先程も述べた通り、main関数の型はint ()もしくは、int (int, char**)のどちらかであることが推奨されています(厳密には実装依存です)。
つまり、mainのプロトタイプはint main()もしくはint main(int, char**)になります。
では、この引数をとる方のmain関数についても説明しましょう。

慣習的に、引数をとるmain関数は、

int main(int argc, char** argv) {
    /* ... */
}

という仮引数名で書かれることが多いです。
この引数はそれぞれ、「コマンドライン引数の数」と「コマンドライン引数リスト」を表しています。
コマンドライン引数は、プログラムを起動する時にOSが受け取るパラメーターです。
bashやcmd.exeなどの中でコマンドを実行するときは、コマンド名に続けてスペース区切りで様々な文字列を入力しますが、コマンド名を含めた全ての入力文字列がコマンドライン引数になり、スペース区切りになった状態でC++のプログラムに渡ってきます。
例えば、

git commit -a

というコマンドを実行した場合、gitというプログラムは"git", "commit", "-a"という3つのコマンドライン引数を受け取ります。引数が3個なのでargcは3になるわけですね。

仮引数argvですが、これは知らないと良くわからない型に見えるかな、と思います。
charというのは文字型の一種なのですが、その後ろにある**は何なのだろう、と思うかも知れません。
*という記号は、先程は乗算を行う演算子として現れましたが、今回は別物です。

まず、型名の後ろに*が付いた型は、ポインター(pointer)型と言います。
ポインタ型は変数のアドレス(address)を指し示す型です。

変数とかアドレスとかポインターの説明

その前に、変数とは何かをちゃんと説明していませんでした。
変数(variable)は、これまた数学の変数から借りてきた言葉です。英語のvariableは、vary(変える)+able(可能)なので、「変えることができるもの」といった意味になるでしょうか。

先ほどの型の説明で、値(value)という言葉を出しました。これもちゃんと説明していませんでした。
例えば0という数。これはint型の値です。もちろん1int型の値だし、42int型の値です。
列挙していけばキリがな――いえ、キリはあるのですが数が膨大過ぎて無理だし何の役にも立たない情報になってしまうのでここには書きませんが、int型で表現された任意の整数は、int型の値である、ということになります。

コンピューター上の状態は全て何かしらの値であると捉えることができます。ハードウェアから考えると、プログラムが直接扱うのは、主にレジスタと呼ばれるCPUが直接扱う高速で小さな記憶域と、RAM(Random Access Memory)と呼ばれるけっこう高速でそれなりの大きさがある記憶域です。RAMは一般には単にメモリと言われることが多いですね。昔は256MBで大容量とか言っていましたが、最近は16GB積んでいても基本的人権扱いされる風潮のようです[要出典]

メモリ領域はそれなりの大きさがあるので、プログラム実行中の内部状態のほとんどはメモリ領域に保持されます。ハードウェアやOSの細かい動きまではさすがにこの文書では説明を省きますが、プログラムが扱うメモリ領域は広いので、番号を使って位置を特定します。これがアドレスです。
メモリ上のデータは、全てその位置を番号で表すことができるのですが、これを人間が直接扱うのは不可能ではないとは言え、とても困難です。
なので、プログラム言語では番号の代わりに名前でデータを特定できるようになっています。これを変数と言います。逆に言えば、変数は何らかの値を表していることになります。ある変数にアドレスを結びつけることを変数の束縛(binding)と言います。

変数を新たに定義するとき、多くの場合は

型名 変数名 = 初期値;
型名 変数名{初期値};

といった記法を使います。初期値がない場合はデフォルト値で初期化されるか、未初期化状態になるかのどちらかです9

例えば、int型の変数を新たに定義する場合は、

int hoge{42};
int piyo;

などとします。この例だと、hoge42という値で初期化されますが、piyoは未初期化です10

変数がどこの位置にあるのかはコンパイラが決めてくれます。実行するまでどこの位置に作られるか分からない変数もあります。
しかし、せっかく名前を付けてアドレスを隠蔽しても、実際に変数がどの位置にあるのかを知りたくなることもよくあります。そのため、C++ではアドレスを取る方法と、アドレスを表す型が用意されています。それがポインターです。アドレスを指し示す(point)ものなので、ポインターです。

アドレスとポインターを混同してしまう人がいるかもしれません。アドレスは、変数と値の関係で言えば値の方です。ポインターは変数です。ポインターが束縛する値はアドレスを表しますが、アドレスはポインターではありません。混乱しやすいので、注意してください。

さて、先ほど、型名の後ろに*をつけると、ポインター型になると言いました。
その型は、*をつけない型の変数のアドレスを示すポインターの型になります。
つまり、char*という型であれば、任意のchar型の変数のアドレスを示すポインターの型、ということになります。

例を見てみましょう。

char a = 'a';
char* b = &a;

'a'UTF-8文字リテラルというもので、char型の値を表します。これは実装定義の1バイト文字です11
&は、変数のアドレスを取得する演算子です。&aであれば、変数aのアドレスを取得しています。
char* b = &aという式全体で、「char型のポインター変数bを定義して、char型の変数aのアドレスで初期化する」という意味になります。

ポインターは変数なので、当然ポインター自身もアドレスを持ちます。
ポインターのアドレスを束縛する変数は、「ポインターのポインター」であるということになります。*を付けるとポインター型になるので、ポインターのポインター型を表すには、*を2つつければ良いことになります。

char**という型は、「char型のポインターのポインター」ということになります。
当然、char**型の変数のポインターはchar***になりますし、さらにどこまでも*を増やして、char*************************************************************みたいな型を使うことも出来ます。
なお、そんな型が登場するコードを見たら私はブチ切れます。ポインターのポインターに親を殺された復讐鬼になって世の中のポインターが全て参照かstd::unique_ptrstd::shared_ptrstd::weak_ptrstd::vectorstd::basic_stringstd::optionalに置き換わるまで戦いをやめないマサカリマシーンと化すので、先鋭的なカルトC++erを怒らせないように気をつけましょう。
お前は何を言っているんだ。

実際のところ、ポインターの機能の多くは今挙げたような標準ライブラリの機能(一部C++17で提案されているものも含む)で代替可能です。気をつけて開発を行えば、生のポインターを使う機会はそれほど多くはないでしょう。

アドレスは*演算子で参照する(dereference)ことができます。まことにややこしい話ですが、*演算子はさっき出てきた乗算の演算子ではなく、また、型名の後ろにつけてポインター型にする*とも異なります。
*演算子でポインターを参照すると、そのポインターに入れられたアドレスが示す位置の変数の値を取ることができます。
先ほどの例で言えば、変数bは変数aのアドレスで初期化されているので、*bとするとaの値を取得できます。

ポインター型を作る時の*記号は、型名との間にスペースがあってもよく、また、変数名との間にスペースがなくても良いです。
つまり、char* bという書き方はchar *bという書き方と同義です。すると、「bの型はchar*である」という情報の他に、「*bの型はcharである」、と見ることもできます。

配列の説明

さて、まだ回り道が続きます。
ポインターは任意の位置のアドレスを示しますが、単なる変数ではなく、配列(array)の先頭を表していることがあります。
配列と言うのは、同じ型の値を連続して並べた領域を指します。配列は、通常の変数と異なり、その要素数(配列の長さ)を変数名の後ろに[]に入れて宣言します。
例えば、

int a[3];

という方法で定義された変数aは、int型の長さ3の配列になります。
この配列の先頭は、a[0]という方法で参照できます。同じように、「配列の先頭+n」番目の要素には[n]を使って参照できます。配列aは長さ3なので、a[0], a[1], a[2]の3つの要素を参照することができます。
ここで気をつけて欲しいのですが、配列の長さは3ですが、a[3]にアクセスしてはいけません。配列の要素を参照する際に[]演算子の中に入れる値を添字(index)と言いますが、これは0を基準にしているので、配列の末尾の要素は「要素数-1」になります。なので、ここでa[3]を参照すると何が入っているか分かりませんし、何が起こるかもわかりません。プログラムがクラッシュする可能性もあります。
配列の要素数を超えた位置にアクセスを行なってはいけません。

さて、配列の要素にアクセスする方法ですが、実はa[0]*aは同じ要素を参照しています。
おや、見覚えのある演算子が出てきました。これは、先ほどポインタの参照を行う時に使ったのと同じものです。つまり、配列変数の指す値を参照すると、配列の先頭の要素が得られるのです。
これはどういうことかと言うと、配列変数は、配列の先頭アドレスを示すからです。配列変数はポインタではないため、アドレスは固定で書き換えはできませんが、参照を行うことはできます。

配列の先頭アドレスをポインターに代入することができます。

int a[3];
int* b = a;

このようにすると、ポインター変数bは配列aの先頭アドレスを示すことになります。
逆に、ポインターbから配列の[]演算子を利用して配列aの要素にアクセスすることができます。

int a[3];
int* b = a;
b[2] = 10; // この場合、a[2] = 10;と書くのと同じ

つまり、ポインターは単一の変数のアドレスを指していることもあるのですが、配列のアドレスを指していることもあります。どちらの意味でポインターが利用されているかは、説明やコードを見てみないと分かりません。これは非常にややこしいため、ポインター以外の方法を使ったほうがいいことが多々あります。

私は常々ポインターはなるべく使うべきではないと考えていますが、それはそれとして、歴史的な経緯によりポインターを使わなければならないケースもあります。main関数の引数もその一つです。
というわけで、main関数の引数はchar**というややこしい型が使用されています。
main関数では、char** argvchar*型の配列のアドレスを示しています。

配列の先頭アドレスだけを示されても、長さはわかりません。
先ほど言ったように、配列の要素数を超えた位置にアクセスを行ってはいけませんが、長さが分からないとどこまでアクセスしても良いかわかりません。
そこで、main関数の場合は、配列の要素数を第一引数で渡しています。argcargvの要素数を示しているのです。

では、argvの示す配列に並べられたchar*型の値は何を示しているのでしょう?
main関数の引数では、char*型の値もまた、char型の配列のアドレスを示しています。
char型の配列は、必ずしもそうであるという訳ではありませんが、多くの場合は文字列を表します。この場合もそうです。
先ほど説明した通り、main関数にはコマンドラインで渡された文字列が渡ってくるわけですが、ここでその文字列は、argvの各要素に順番に格納されます。

文字列も配列なので、要素数を超えた場所にアクセスしてはいけません。ところが、argvが示すchar*型のポインター配列の要素数はargcで得ることができますが、各文字列の要素数は渡されていません。
実は、C++には(C言語からの遺産として)「長さの不明な文字列」がよく出現します。
長さ不明といっても、実際には長さはあるのですが、先頭から辿っていかないと長さを求められないのです。
そういった文字列はnull終端されています。つまり、文字配列の一番最後にchar型の値で言えば0、文字リテラルで表すなら'\0'に相当する文字が入っています。なので、その最後の文字が出現するまで、先頭から順に数えていかないといけません。

C++では、もっと文字列が扱いやすい標準ライブラリの文字列クラスが用意されていますので、そちらを使ったほうが良いでしょう。null終端された文字列から変換することも可能です。

さて、ここまででmain関数の説明は8割方終えることができたと思います。まとめましょう。

  • main関数は、プログラムの初期化終了後に呼ばれる関数である
  • main関数は、int main()もしくはint main(int argc, char** argv)という形のどちらかで定義する
  • main関数が引数をとる場合、第一引数はコマンドライン引数の数、第二引数はコマンドライン引数の文字列の配列である

えー、なんか現時点で文字数が2万字に近くなっているようですが、続けます。

using namespace std;

プログラミングを行っていると、色々なところで名前が登場します。main関数はmainという名前を持った関数ですし、argcargvはそういう名前を付けられた仮引数です。
こうした名前を識別子(identifier)と言います。

さて、C++は世界中の人に利用されていて、ありがたい事に素晴らしいライブラリが無料で公開されています12。おかげで私のような平凡プログラマーでも楽に開発ができるのですが、他の人が作ったライブラリを利用するということは、他の人が定義した識別子を自分のプログラムに取り込むということでもあります。
そして、何の相談もせずに作られたライブラリで、同名の識別子が使われているのは非常によくあることです。それどころか、一人の人が作っていても、うっかり別の場所で同じ名前を使ってしまう、なんてこともないとは言えません。
そういった、名前の衝突を避けるために、名前空間(namespace)というものが用意されています。

名前空間を作るのは簡単です。

namespace ns {
}

このように書くと、{}で囲まれたブロックの内部が、nsという名前空間になります。名前空間で定義された識別子は、他の名前空間で定義された識別子と別物になります。また、名前空間はネストさせることができます。
ちなみに、一番外側の空間はグローバル名前空間(global namespace)と呼ばれます。

int a; // (1)
namespace x {
    int a; // (2)
}
namespace y {
    int a; // (3)
}

上記の例では、(1), (2), (3)のaは全て異なる変数です。
実際にaという名前の変数を使おうとした時に、C++のコンパイラーは名前探索(name lookup)を行います。名前探索では、現在のブロック内で宣言された識別子が優先的に見つかります。上記の例で言うなら、x名前空間内で名前探索を行った場合、まず(2)が見つかりますし、y名前空間内で名前探索を行った場合、(3)が見つかります。そして、xの中でもyの中でもない場所で名前探索を行うと、(1)が見つかります。

逆に、名前空間内の識別子を名前空間の外から使おうとすると、一部の例外を除いて名前探索は行われません13。その場合、名前空間を明示して識別子を利用することができます。
上記の例では、x::aと書くと(2)を、y::aと書くと(3)を示すことになります14。また、グローバル空間にあることを明示したければ、::aと書くと、必ず(1)になります。

ライブラリ製作者は、名前の衝突を防ぐために、自作の関数や型などは全て名前空間に入れるようにします。少なくとも、行儀のいいライブラリならそうなっています。こうすれば、名前空間名さえ衝突しなければ、名前の衝突を気にする必要はありません。

C++の標準ライブラリもまた、名前空間の中に入れられています。標準ライブラリの名前空間はstdです。std名前空間の他に、std<n>(<n>は0以上の整数)という名前は標準ライブラリの将来の実装のために予約されているので、標準ライブラリ以外が使ってはいけません。std2とかstd42とかstd65535とかも含めて全てです。今のところ、stdしか使われていませんが。

さて、こうして名前空間に入れることで衝突を回避した名前ですが、一々名前空間まで含めた名前を書くのは面倒だという向きもあります。そこで、using namespace文が用意されています。
using namespace std;と書くと、std名前空間内の識別子は、名前空間外であっても名前探索の対象となります。その代わり、std名前空間の識別子と他の識別子が衝突した場合、エラーが発生することもあります。
main関数の中でusing namespace std;を書くと、

  • std名前空間で定義されている識別子を利用する
  • std名前空間が名前探索の対象となるのはmain関数だけである

ということになります。

よくある"Hello, World!"プログラムで、using namespace std;が「おまじない」としてグローバル名前空間に書いてあることがあります。以下のようなコードです。

#include <iostream>

using namespace std;

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

グローバル空間でusing namespaceは使うべきではありません。これを行ってしまうと、せっかく名前空間の中に閉じ込めたのが無駄になってしまいます。
それでも、ソースファイル一個の中であれば他の翻訳単位に影響を及ぼさないためまだマシですが、ヘッダーファイルのグローバル名前空間でusing namespaceを使うのは最悪です。
この問題に関して、私はグローバル名前空間でusing namespace std;を乱用している入門記事が悪いと常々思っています。

強い思想が漏れました。
それはそれとして、using namespaceは、狭い範囲で使うならそれなりに便利です。たとえば関数一個の中に限定するとか、そういう使い方なら問題ありません。
また、C++11から入ったユーザー定義リテラルを使用するためにusing namespace std::literals::chrono;といったコードを書くのも、どこからもincludeされないソースファイル内や自分が定義した名前空間内であれば構わないでしょう。

ただ、std名前空間程度であれば、わざわざusing namespace std;を使うより、std::を付けて書いても良いのではないか、と私は思っています。
それと、using namespaceで名前空間全体を取り込んでしまうと問題が発生することもありますので、使いたい識別子が限定されているなら、using std::size_t;といったように、using namespace文ではなく、using文を使うこともできます。

また、世の中には多重に入れ子になった長い名前空間があって、そういう時は「名前空間を一々書くのは面倒だ、using namespaceしてしまえ」となることもあるでしょう。
ですが、例えばvery::very::very::loooooooong::name_spaceなどといった長い名前を使う時でも、

namespace ns = very::very::very::loooooooong::name_space;

という方法で名前空間エイリアスを使うことができますので、基本的にはそちらを推奨します。

cout << "Hello World!" << endl;

この文は、意味としては、「標準出力に『Hello World!』という文字列を書き出して改行する」ということなのですが、きちんと理解するためには、

  • リテラルについて
  • coutが何者なのか
  • テンプレートについて
  • オペレーターオーバーロードについて
  • マニピュレーターについて

といったことを知らなければいけません。

リテラルについて

リテラルというもの全般について、まだちゃんと解説していなかった気がします。
C++の文法には、データを直接埋め込むことができる方法があります。これまでにもちょっと出現した'a'のような文字リテラルや、0, 42といった数値も、ソースコード上に現れたものは整数リテラルといいます。
ユーザー定義リテラルというものもあるので、リテラルの種類はそれこそ無限にあるのですが、基本的に、コア言語機能としては以下のものが用意されています。

  • 整数リテラル
    • 10進整数リテラル
    • 8進整数リテラル
    • 16進整数リテラル
    • 2進整数リテラル
  • 浮動小数点数リテラル
  • 文字リテラル
  • 文字列リテラル
  • 真偽値リテラル
  • ヌルポインタリテラル

整数リテラル

整数リテラルは、文字通り整数型の値を表すリテラルです。何度も例に出した042といったものは、すべてint型の整数リテラルです。
整数リテラルには前述のリストのように複数の種類があります。

10進整数リテラル

1〜9で始まり、その後に0〜9の数字が続くリテラルは10進整数リテラルです。
例えば、1242などが該当します。
ここで注意したいのは、0108などは10進整数リテラルではないということです。特に後者は、正しい整数リテラルでもありません。
また、このルールに従うと、0は10進整数リテラルではありません。

8進整数リテラル

では0は何かと言うと、これは8進整数リテラルに分類されます。
8進整数リテラルは、0から始まり、その後に0〜7の数字が続くリテラルです。
数値を8進数で表すので、010は10進リテラルにおける8と同じことになります。07の次は桁上がりをしてしまうので、0809はありません。
10進整数リテラルと紛らわしいのであまり使われませんが、定義により0は8進整数リテラルなので、このことを知らない人も知らず知らずの内に使っているのではないかと思います。

16進整数リテラル

先頭が0xで、その後に0〜9もしくはa〜fが続くリテラルを16進整数リテラルと言います。アルファベットの大文字小文字は区別されません。
コンピューターが扱う数値は2進数なので、2の4乗を基本とする16進数は扱いやすい数値です。
例えば0xffという16進整数リテラルは、10進整数リテラルの255と同じ数値を表します。

2進整数リテラル

先頭が0bで、その後に0もしくは1が続くリテラルを2進整数リテラルと言います。bは大文字でも小文字でも構いません。
例えば、0b1101というリテラルは、10進整数リテラルでは13になります。0b11111111と、ここまで1を並べてようやく0xffと同じ値です。
ビット演算の際には出現するかもしれません。

リテラルサフィックス

C++には整数型がいくつかあり、それぞれ表せる値の範囲も異なります。また、符号付きの型と、符号なしの型があります。
符号付きと符号なしというのはどういうことかと言うと、その整数型で負の数を表せるかどうかということです。負の数を表すことができるのが符号付きの型、負の数を表すことができないのが符号なしの型です。

標準で用意されている型は、以下のようになります15

  • 符号付きの型
    • signed char
    • short
    • int
    • long
    • long long
  • 符号なしの型
    • unsigned char
    • unsigned short
    • unsigned int
    • unsigned long
    • unsigned long long

それぞれの型が表せる数値の範囲は、
signd charshortintlonglong long
unsignd charunsigned shortunsigned intunsigned longunsigned long long
となっていて、消費するメモリ量は、
signd char = unsignd charshort = unsigned shortint = unsigned intlong = unsigned longlong long = unsigned long long
になります。

このうち、int, long, long long, unsigned int, unsigned long, unsigned long longに関しては、整数リテラルで表すことができます16
実は今まで説明してきたリテラルはすべてint型のリテラルでしたが、それぞれのリテラルにサフィックス(suffix)をつけることで、別の型のリテラルにできます。サフィックスには以下の種類があります。

  • u:符号なしの型にします
  • llong型にします
  • lllong long型にします

サフィックスのulまたはllは同時に使うことができます。また、順番はどちらでも構いません。
以下、0にサフィックスをつけた例と型名を挙げます。

|サフィックス|例|型名|
|:-|:--|:--|
|l|0l|long|
|ll|0ll|long long|
|u|0u|unsigned int|
|ul, lu|0ul|unsigned long|
|ull, llu|0ull|unsigned long long|

浮動小数点数リテラル

浮動小数点数は、整数では表すことの出来ない小数点以下の数や、逆にとても大きな数までを表すことのできる型です。
C++には、以下の3つの浮動小数点数型が用意されています。

  • float
  • double
  • long double

浮動小数点数リテラルは、以下のように表記することができます。

整数部分.小数部分e指数

eは大文字でも小文字でも構いません。
このうち、整数部分と小数部分のどちらかおよびeから後ろの部分は省略可能です。
例えば、以下のようなリテラルが浮動小数点数リテラルになります。

3.14
1.5e10
3.8e+2
2.3e-10
1.
.45

指数部分は、10を底とした指数を表します。例えば、1.5e10であれば、

1.5 \times 10^{10}

を表すので、150億ということになります。
逆に2.3e-10

2.3 \times 10^{-10} = 2.3 / 10^{10} 

ということになるので、0.00000000023を表していることになります。
省略した場合は、どの部分も0であると解釈されます。
1.1.0e0, .450.45e0と同義です。

浮動小数点数リテラルにもサフィックスがあります。
浮動小数点数の場合は、fもしくはlのどちらかです。
fを付けるとfloat型、lを付けるとlong double型のリテラルになります。どちらも付いていなければdouble型です。

文字リテラル

既に登場していますが、文字リテラルは'a'のような''で囲まれた文字で表されるリテラルです。
文字には文字コードという番号が割り当てられていて、文字リテラルは実際にはその番号を表します。
文字を表す型の一つにchar型がありますが、char型の値は実際には整数です。
ただし、char型の文字がどのようなエンコーディングであるのかは、C++の規格では定められていません。
いわゆるASCII互換の文字コードでは'a'char(0x61)ですが、そうでない環境もあるかもしれません。

C++の文字型には下記のものがあります。

  • char
  • wchar_t
  • char16_t
  • char32_t

このうち、char16_tchar32_tは、文字のエンコーディングが定められています。それぞれ、UTF-16とUTF-32です。
一方、charwchar_tはどのようなエンコーディングの文字列か定められていません。また、wchar_tはワイド文字型と呼ばれ、charより大きいサイズの型であることは定められていますが、環境によって大きさが異なるため、環境依存性のないコードを書くときにはあまり使えません。

UTF-8の文字型はないのかって? ないです(2016/12/6現在)
頼むから……入れてくれ……。

さて、'a'といったリテラルはchar型の文字リテラルですが、wchar_t, char16_t, char32_tの文字リテラルも存在します。
整数リテラルの場合、サフィックスを付けると別の型にできましたが、文字リテラルの場合はプレフィックス(prefix)をつけると別の文字型になります。

プレフィックスには以下の種類があります。

  • L: wchar_t型の文字を表す
  • u: char16_t型の文字を表す
  • U: char32_t型の文字を表す

プレフィックスは大文字と小文字を区別します。
L'a', u'a', U'a'というリテラルはそれぞれwchar_t, char16_t, char32_t型の値を表しています。

なお、C++17では文字リテラルにもu8プレフィックスが導入される予定です。これは、UTF-8でエンコーディングされたchar型の値1つに収まる文字を表します。
UTF-8は可変長エンコードで、1文字を表すのに2バイト以上必要な場合もあります。したがって、u8プレフィックスを付けても表せない文字もあります17

文字列リテラル

先程も触れましたが、文字列は文字の配列です。
文字型がchar, wchar_t, char16_t, char32_tと存在するので、それぞれの型に対する文字列がやはりあります。

文字列リテラルは、""で囲まれた範囲で表されます。
"abcde"というリテラルは、'a', 'b', 'c', 'd', 'e', '\0'という6個のchar型の値が並んだ配列を表します。
最後に追加されている'\0'はnull文字です。CやC++では、文字列はnull終端されていることが前提になっていることが多いため、文字列リテラルもnull終端のために1文字追加されます。歴史的な経緯というやつです。

文字型と同じように、文字列型にもプレフィックスをつけることができます。
プレフィックスの種類も先ほど紹介したものと同じです。ただし、u8プレフィックスは文字列リテラルにはC++11の時点で導入されているので、今でも使えます。

プレフィックスのない文字列リテラルと、Lプレフィックスが付けられたワイド文字リテラルは、文字型がそうであるのと同じようにエンコーディングの指定がありません。
uUプレフィックスがついた文字列リテラルは、それぞれchar16_t型とchar32_t型で、UTF-16とUTF-32にエンコーディングされます18
u8プレフィックスがついた文字列リテラルは、文字型はcharの配列ですが、エンコードだけはUTF-8になります。エンコードが違うのに、例えば文字列を引数として受け取っても型は同じなので判別できません。
標準化委員会の文字コードに関する理解が浅くて設計が悪いのだと思います。

raw文字列リテラル

C++のソースコードでは、\記号を使うことでエスケープ(escape)ができます。これは文字リテラルの中でも同様に使えます。これらをエスケープシーケンスといいます。19

全てを列挙はしませんが、

  • \n : 改行(LF)
  • \r : 改行(CR)
  • \t : タブ文字
  • \\ : \記号そのもの
  • \" : "記号
  • \' : '記号
  • \ooo : 文字コードの8進数指定
  • \xhh : 文字コードの16進数指定

などがあります。中にはあまり使わない文字もありますが、"\\n"あたりは使うことも多いのではないでしょうか。

エスケープシーケンスは特殊な文字を埋め込む等には便利なのですが、一方で、わざわざエスケープを行わなければいけなくなってしまうこともあり、文字列中に"が多数出現する場合(例:JSON文字列を埋め込む時)など、可読性を下げてしまいます。
そのため、raw文字列リテラルというものが用意されています。
raw文字列リテラルは、R"(文字列)"という形式で書かれた文字列で、この内部では\文字がエスケープされることもありませんし、"や改行などもそのまま書けます。
R"(から始まったraw文字列は)"で終了するので、)"という文字の連続が現れることはできません。ただし、"(の間、)"の間には任意の同じ文字列を入れることができて、例えばR"a()a";といった書き方をすれば、)a"という文字の並びが出てこない限り終了しません。

R"(abc\def)""abc\\def"と同じ文字列になりますし、
R"a(abc)"def)a""abc)\"def"と同じ文字列を表します。

const char * xhtml = u8R"(<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Hello, World!</title></head>
<body><div>Hello, World!</div></body>
</html>)";

なんてものを書くこともできます。
上記の例でお分かりかと思いますが、raw文字列リテラルにもプレフィックスをつけることができます。意味は同じです。

文字列リテラルは配列なのでアドレスがあり、メモリ上のどこかに保存されています。実行中に書き換えられてはいけないので文字列リテラルの型にはconst修飾子がつきます。const修飾子が付いた型に対して書き換え操作を行おうとすると、コンパイルエラーになります。

文字列リテラルの型は、const 文字型[文字列長+1]というものになります。+1は終端のnull文字の分です。
例えば、"abcde"という文字列は、大抵文字コードならアルファベットはchar型一個に収まるので、const char[6]という型になります。

真偽値リテラル

C++には、真もしくは偽のどちらかの値しか取ることができない真偽値(boolean)型というものが用意されています。型名はboolです。
bool型は、真の状態をtrue、偽の状態をfalseという値で表します。真偽値リテラルは、まさにtrueもしくはfalseのどちらかです。
どちらかというと単なる識別子のように見えるかもしれませんが、truefalseは変数ではなくリテラルです。ただの値なのでアドレスは存在しません。

nullポインタリテラル

C++には、nullポインターを表すリテラルが用意されています。nullポインターとは、有効なアドレスを示していないポインターのことです。
ポインターに何らかの変数のアドレスを入れる前に、nullポインターで初期化されることがあります。
nullポインタリテラルはnullptrという文字列です。これもまた、真偽値リテラルと同じように変数ではないのでアドレスは存在しません。

coutが何者なのか

coutは、正確に名前空間まで含めて書くと、::std::coutです。std名前空間の中にあるので、標準ライブラリで定義されていることが分かると思います。
using namespace std;を使っているので、名前空間は省略できます。

coutが何かと言うと、まずこれは変数です。もっと言うなら、グローバル変数です。
coutの型はstd::ostreamというものです。std::ostreamは、標準ライブラリで定義されたストリーム出力クラスです。

標準入出力・ファイル入出力・ネットワーク・ソケットなど、プログラムは様々な方法でデータを入出力します。そこら辺はOSが機能として提供してくれるので、一般的なアプリケーション開発では内部実装まで詳しく気にする必要はそれほどありません。
C++の標準ライブラリでは、そういった入出力操作を「ストリーム」というものに抽象化することで、出力先がなんであろうと同じ方法で出力をできるようにしています。

std::ostreamクラス(class)です。厳密に言うなら実体化されたテンプレートクラスですが、テンプレートの解説はこの後に行うことにします。
クラスは、開発者が新しく型を定義するための機能です。これまでに出てきたintcharといった型はC++の言語機能として予め用意された型ですが、クラスは開発者が定義を行うことができます。

クラスは、データメンバーとメンバー関数を持つことができます。
データメンバーは、クラスの内部状態を保存するための変数で、メンバー関数は、クラス型の値に対する操作を定義した関数です。

例えば、std::ostreamは出力を行うクラスなので、そのためのメンバー関数writeが用意されています。これを使って、一定の長さのデータを出力することができます。

cout.write("abcdef", 3);

クラスのメンバーには上記のように、.演算子を利用してアクセスできます。
writeメンバー関数の第一引数は出力するデータ(const charの配列)のアドレスで、第二引数は出力するデータの個数です。上記のような例であれば、"abc"の3文字が出力されることになります。

しかし、データの書き込みの度に、一々writeを呼び出して、データのポインタと長さを与えて、という操作を行うのは不便です。
そこで、ostreamクラスに対する<<という演算子が用意されています。

オペレーターオーバーロードについて

これまでにも、演算子(operator)というものはいくつか登場しました。
int型のような組み込み型には、あらかじめ用意された演算子があります。
それとは別に、開発者がクラスなどの独自定義した型に対し、演算子を定義することができます。これをオペレーターオーバーロードと言います。

演算子は、組み込み型のものを除けば、ちょっと特殊な関数もしくはメンバー関数であると考えて差し支えありません。
C++の関数は、同じ名前のものであっても、引数の型が異なれば複数個定義できます。これをオーバーロード(overload)と言います。オペレーターオーバーロードも同じです。

演算子をオーバーロードするには、

  1. クラスのメンバー関数として定義する
  2. フリー関数(グローバル関数)として定義する

という2種類の方法があります。
それぞれ、例を示します。

struct A {
    int i;
    A operator +(A rhs) { return {i + rhs.i}; }
};

struct B {
    int i;
};

B operator +(B lhs, B rhs) { return { lhs.i + rhs.b }; }

structキーワードは、クラスを定義する際に使うキーワードの一つです。クラスを定義するキーワードにはもう一つ、classキーワードもあります。structはもともとC言語から存在していたもので、structキーワードを使って定義されたクラスは一般的に構造体と呼ばれます。classstructは、デフォルトのアクセス指定子20publicprivateかの違いがありますが、他に差はありません。

構造体Aと構造体Bは、それぞれint型のデータメンバーiを持っています。構造体Aは、メンバー関数としてoperator +(A)を持っていて、構造体Bにはメンバー関数がありませんが、フリー関数のoperator +(B, B)があります。

演算子が定義されていれば以下のように、

A a1{10}, a2{15};
A a3 = a1 + a2;
B b1{20}, b2{25};
B b3 = b1 + b2;

という風に、を使うことができます。ここでは他の演算子は定義されていないので、a1 - a2のような計算はできず、コンパイルエラーとなります。
ちなみに、独自定義した演算子は、

a3 = a1.operator +(a2);
b3 = operator +(b1, b2);

などのように、関数として呼び出すこともできます。しかし、普通はこの方法が必要になることはないでしょう。

さて、オペレーターオーバーロードについてざっくり説明したところで、std::ostream<<演算子について説明します。
std::ostreamの値に対して<<演算子を利用すると、渡したオブジェクトが文字列として出力されます。
"Hello, World!"なら元々文字列ですが、例えばint型の値であっても、文字列に変換してから出力されます。
とは言え、C++のオブジェクトはいくらでも定義できるし、operator <<が全てのオブジェクトに対して定義されている訳ではありません。operator <<の定義されていない型の値を渡すとエラーになります。
ただし、operator <<の定義を追加することはできます。

std::cout << "Hello, World!"という式を実行すると、"Hello, World!"がそのまま表示されます。これは、std::ostreamconst char*型を受け取るoperator <<を実装しているからです。
さて、std::ostreamに対する<<演算子は、std::ostream参照(reference)を返します。独自に定義した演算子であっても、同じようにするべきだろうと思います。参照とは、ざっくりと言ってしまえば、コピーを行わずに変数の受け渡しを行うために用意されている機能です。
std::ostreamに対する<<演算子は、出力操作を行った後、出力先となったstd::ostream自身を返します。すると、<<演算子で再び出力を行うことができます。

つまり、cout << "Hello, World!" << endl;という文は、
(cout << "Hello, World!") << endl;という風に書いても同じであり、もっと変形するなら
cout.operator <<("Hello, World!").operator <<(endl);と書いてもよいのです。
冗長なのでそんな書き方誰もしないと思いますが。

マニピュレーターについて

cout << "Hello, World!"までは解説しました。
しかし、この文はまだ終わっていません。後ろに<< endl;があります。
<<が演算子なのは既にお分かりだと思います。endlについてですが、これもstd名前空間の中で定義されている識別子です。ですので、using namespace std;を使わない場合は、std::endlと書かないといけません。

std::coutが変数だったので、これも変数かな?と思うかも知れませんが、違います。
では何かといいますと、std::endlは関数です。
std::ostreamには関数を受け取るoperator <<が実装されていて、そこに渡す関数をマニピュレーターと言います。
マニピュレーターは標準で何種類も用意されていて、それぞれ別の挙動をします。そのため一概にどうとは言えないのですが、std::endlに関して言えば、

  1. 改行文字を出力する
  2. 出力ストリームのバッファをフラッシュする

という操作を行います。

実は、std::ostreamは内部にバッファを持っていて、<<演算子の呼び出し時にはバッファに書き込みを行い、出力は行わないことがあります。最終的にはいずれバッファの中身は出力されますが、バッファにデータが貯められている間は、出力先はデータを受け取ることができません。
そのため、flushというメンバー関数が用意されていて、そちらを利用することでバッファの中身を強制的に出力させることができます。std::endlマニピュレーターは、改行すると同時にflushも実行してくれるので、例えば対話式のCLIツールを作る場合など、メッセージを一行出力してから入力待ちを行うようにしたりできます。

標準ライブラリには、他にも様々なマニピュレーターが用意されていて出力フォーマットを変えたりできますが、一番良く使うのはstd::endlでしょう。

テンプレートについて

さて、std::endlは関数だと言いましたが、実はこれはテンプレート関数です。
また、std::coutの型もテンプレートクラスであると先ほど述べました。

C++の文字型にはchar, wchar_t, char16_t, char32_tの4種類があります。
それぞれ別々の型なので、それぞれの型の文字列に対して出力操作を考えると、普通に考えれば4種類の実装が必要ということになります。
しかし、文字列を出力するというのは要するに一定長のデータの配列を出力するということで、大体同じような操作になるわけです。
それなのに、4種類の実装を作るのは無駄が多いです。
ではどうするか。

テンプレートを使いましょう、というのがC++の解決策です。
テンプレートは、クラスや関数、変数などを、複数の型ごとに定義するための雛形です。
例えば、std::ostreamはこのように定義されています。

namespace std {
typedef basic_ostream<char> ostream;
}

typedefというキーワードは、型の別名を定義するためのものです。std::ostreamは実はbasic_ostream<char>の別名なのです。
そして、basic_ostreamというのが、テンプレートクラスです。

namespace std {
template<typename CharT, typename Traits = char_traits<CharT>>
class basic_ostream;
}

というのが、std::basic_ostreamの宣言です。
このbasic_ostreamchar型に関して実体化したものの別名が、std::ostreamです。

std::endlもテンプレート関数です。

namespace std {
template<typename CharT, typename Traits>
basic_ostream<
    CharT, Traits
> &endl(basic_ostream<CharT, Traits>&);
}

テンプレート関数は、引数によって型が推論されます。なので、std::endl(std::cout)を実行すると、std::endl<char>が呼ばれます。
また、逆に、std::ostream<<演算子は、ostream& (ostream&)型の関数へのポインタを受け取るので、std::cout << std::endlを実行すると型が推論されて、std::cout.operator<<(std::endl<char>)に解決されます。

さて、これでようやく、"Hello, World!"を出力するところまで来ました。あと少しです。

return 0;

普通の関数であれば、戻り値は呼び出し側で受け取って、その後何らかの処理に使うことができます。
ではmain関数の場合は?

main関数は、int型の戻り値を返さなければなりません。
main関数の終了後、C++のプログラムは自動的にstd::exit関数を実行します。main関数の戻り値は、ここで使用されます。
std::exit関数は、

namespace std {
[[noreturn]] void exit(int status);
}

という関数です。新しく[[noreturn]]というものが現れましたが、これは属性(attribute)というC++11から追加された機能です。[[noreteurn]]属性は、「この関数は呼び出したら戻ってこない」ということを示しています。

std::exitは、グローバル変数のデストラクタ呼び出しなどの終了処理を行った後、プログラムを終了させる関数です。std::exitに渡されたint型の値は、終了ステータスとしてホストプロセス21が受け取ります。
プログラムが正常終了したことを通知するには、0もしくはEXIT_SUCCESSマクロを渡す必要があります。また、異常終了した場合はEXIT_FAILUREマクロを渡さなければいけません。それ以外の値がどう振る舞うかは実装依存です。注意しましょう。

通常、関数は戻り値を返すために、return文を書かなければいけません。戻り値の型がvoidである22場合以外は、何かを返す必要があるのに何も返さないと、コンパイルエラーになる場合が多いですし、未定義動作を引き起こす可能性があります。
ただし、main関数に関してのみ、例外的にreturn文を書かないことが許されていて、return文を書かない場合は0を返すのと同じ意味になります。

つまり、実はreturn 0;は書かなくても良いです。あえて書いたのは、このことを説明するためでした。

'}'

関数ブロックの閉じ括弧です。
さすがにこれを説明する必要はあまりないかと思うので、あとがきでも書きます。

現在、12/6 22:34です。
超ギリギリです。すみません。思いの外大ボリュームになってしまい、いくら書いても解説が終わらない地獄に陥りました。
それでもかなり駆け足気味になったし分かりにくい部分も多いと思うので、いずれもっとちゃんと整理したいなと思っています。

そんな訳で、一応ここまでにしようかと思います。
それではみなさん、良いクリスマスを。

明日は、@wolf_cppさんの「深夜テンションで再帰について書くことになった」です。


  1. 私は、その最たる例がusing namespace std;の濫用だと思っています。 

  2. ただし、これは実装によりけりで、必ずしもこれらの過程が一致するわけではありません。最終的に、正しく動作する実行ファイルが出来上がればいいわけですからね。 

  3. 例:Win32 APIのmin, maxマクロと標準ライブラリのstd::min, std::max関数 

  4. フリースタンディング環境を除く 

  5. 関数が呼び出される前に、暗黙的に別の型の値に変換されることはあります。 

  6. 少なくともこの場合は 

  7. テンプレートやinline関数など、例外もあります。 

  8. グローバル変数のコンストラクタとデストラクタや初期化式など、main関数が呼ばれる前や終わった後に行われる処理もあります。 

  9. ユーザー定義のデフォルトコンストラクタを持たない型は、初期化を行わない場合、内部にどのような値が入っているかは分かりません。パフォーマンス上の理由であえて未初期化状態のままにすることもありますが、基本的には必ず行うようにするべきです。 

  10. global変数やstatic変数など、初期化式を書かなくても自動的に0に初期化される場合もあります。 

  11. 'a'は"a"という文字を表しますが、実装定義なので厳密に互換性があるとは言えません。C++17で提案されているu8文字リテラルでは必ずUTF-8にエンコーディングされるので、そちらを使うべきですが、C++17はまだ標準化されていないので例に示していません。 

  12. C++に限った話ではありませんが 

  13. 関数名の探索時には、ADL(argument dependent lookup: 実引数依存の名前探索)というルールで名前空間内の関数が呼び出されることがあります。 

  14. 正確には、この例ではxyといった名前は探索されます。完全に名前を特定するためには、::x::aという風に、グローバル空間から完全修飾された名前を指定する必要があります。 

  15. いくつかのルールにより同じ型に対して複数の記法がとれる(例:shortsigned short intと書いても良い)ものもありますが、簡単にするために省略してあります。 

  16. signed char, unsigned char, short, unsigned shortに関しては、リテラルで表すことはできません。int型から代入を行う場合は暗黙的な型変換が行われますが、どうしてもそれらの型の値がほしい場合は、キャストするしかないでしょう。 

  17. もっとも、UTF-16やUTF-32でもサロゲートペアや合字など、表せない文字はあります。 

  18. 文字列リテラルの場合はサロゲートペア利用文字等、単一の文字型で表せない文字も含めることができます。 

  19. 勘違いされることが多いのですが、エスケープシーケンスは文字列外でも利用できます。 

  20. アクセス指定子は、メンバーがクラスの外から参照できるかどうかを示すキーワードです。private指定されたデータメンバーやメンバー関数は、クラス外から利用できません。 

  21. プログラムを実行した親プロセスのこと。 

  22. これは戻り値を返さないという意味になります 

kazatsuyu
だいたいC++er。constexpr好き。最新規格とか追いかけるの好き。人類ははやくSFINAEを捨ててconstraintに移行すべき。 Rustもやっている。他:Haskell, TypeScriptなど
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした