このページでは、コメントという機能を取り上げます。これまでに取り上げてきた機能とちがって、コメントはプログラムの実行結果には何ら影響を与えません。コメントは、ソースコードを書いたり読んだりする人間に向けた機能です。
このページの解説は C++14 をベースとしています。
以下は目次です。要点だけをさっと確認したい方は、「まとめ」をご覧ください。
人間がソースコードに書かれている内容を読み解くとき、変数📘や constexpr変数につけられた名前(識別子📘)が助けになります。
しかし、あまりにも長くて説明的な名前は、かえって読みづらいソースコードになってしまいますし、使える文字の制約(「定数式と識別子」のページを参照)もありますから、限界があります。
そこで、より直接的に、しかも日本語などの人間が読みやすい言語を使った説明やメモをソースコード内に書き込む方法があります。このような説明文やメモのことを、コメント(注釈)📘(comment) と呼びます。
コメントはソースコードに書き込みますが、コンパイル時📘にコメントの部分は無視されます。そのため、プログラムの動作に影響を与えません。
【上級】正確には、コメントはコンパイルに先だって1つの空白文字に置き換えられます。
プログラマーが書き込んだコメントを、コンパイラにコメントであるとみなしてもらうには、正しい記法で書く必要がありますが、内容自体は好きにできます。
コメントには、この後紹介する2つの記法があります。2つの記法がソースファイル内に混在してあらわれても問題ありません。
1つ目は、//
のように2つのスラッシュを並べて書き、その後ろにメモを記述する方法です。/
は除算で使う記号と同じですが、2つ並べるとコメントの記号になるということです。
この記法では、その行の終わりまでがコメントと認識されます。
実際のプログラムの例は次のようになります。「文字列の入力」のページで登場したプログラムにコメントを追加してみます。
#include <iostream>
#include <string>
int main()
{
// 整数の入力を受け取って、そのまま出力する
std::cout << "Please enter an integer.\n";
int value {};
std::cin >> value;
std::cin.ignore(); // 最後に入力されてしまう改行文字を無視する
std::cout << value << "\n";
// 文字列の入力を受け取って、そのまま出力する
// 空白を含んだ文字列でも受け取れるように、std::getline関数を使っている
std::cout << "Please enter a string.\n";
std::string s {};
std::getline(std::cin, s);
std::cout << s << "\n";
}
実行結果:
Please enter an integer.
100 <-- 入力した整数。最後に Enterキーを押している
100
Please enter a string.
abc <-- 入力した文字列。最後に Enterキーを押している
abc
コメントを記述する場所は自由です。あるコメントが、ソースファイル内のどの部分の説明やメモになっているのかが分かりやすいようにしましょう。
たとえば、ある1つの文に対するコメントなら、その行の上側や右側に書きます。サンプルプログラムでは、std::cin.ignore();
に対するコメントがその例です。
また、複数の文からなるコードに対するコメントは、そのまとまりの上側に書きます。「// 整数の入力を受け取って~」や「// 文字列の入力を受け取って~」がこれに当たります。説明が長くなるなら、コメントを複数行に渡って書きます。横に長くなることを受け入れて、1行に書いてしまう手もありえますが、右側にスクロールしないと全容が把握できなくなる恐れがあり、読み手に対して不親切です。
コメントの書き方の2つ目は、/*
と */
という2文字の記号のペアを使う方法です。この記法では、このペアで挟まれた範囲にメモを記述します。
【C言語プログラマー】C言語に古くからあるコメントスタイルと同じものです。
この方法では、次のように記述します。
#include <iostream>
#include <string>
int main()
{
/* 整数の入力を受け取って、そのまま出力する */
std::cout << "Please enter an integer.\n";
int value {};
std::cin >> value;
std::cin.ignore(); /* 最後に入力されてしまう改行文字を無視する */
std::cout << value << "\n";
/* 文字列の入力を受け取って、そのまま出力する
空白を含んだ文字列でも受け取れるように、std::getline関数を使っている */
std::cout << "Please enter a string.\n";
std::string s {};
std::getline(std::cin, s);
std::cout << s << "\n";
}
実行結果:
Please enter an integer.
100 <-- 入力した整数。最後に Enterキーを押している
100
Please enter a string.
abc <-- 入力した文字列。最後に Enterキーを押している
abc
この方法は、//
による方法よりも記述量が多くなるので、多少面倒です。そのため、C++ プログラマーのほとんどは //
を好んで使います。ただし、/*
と */
による方法にも利点があります。代表的な2つの利点を紹介します。
1つめの利点は、/*
と */
による方法では、行の一部分だけをコメントにできることです。//
による方法ではこれはできません。
「定数式と識別子」のページで登場したプログラムで試してみます。
#include <iostream>
int main()
{
constexpr auto member_num = 10; // 会員の人数
constexpr auto absent_num = 1; // 欠席者の人数
constexpr auto entry_fee = 1000; // 参加費
// 徴収した参加費の合計を出力
std::cout << (member_num /*- absent_num*/) * entry_fee << "\n";
}
実行結果:
10000
std::cout を使っている行の途中にコメントがあります。コメントになっているのは、- absent_num
の部分だけです。コンパイル時にはコメントの部分が無視されるので、この行は、std::cout << (member_num ) * entry_fee << "\n";
と書かれているのと同じということになります。したがって、計算結果に「欠席者の人数」が影響を与えないことになります。
このように、コメントを使って、これまで機能していたソースコードの一部分を無視させる使い方を、コメントアウト (comment out) といいます。また、コメントアウトしていた部分を元の有効な状態に戻すことを、アンコメント (uncomment) といいます。
最終的に「欠席者の人数」を使わないことにしたのなら、ソースコードからコード自体を消すべきです。余計なものを、それが余計であることが分かりづらいかたちで残さないほうがいいです。
2つ目の利点は、/*
と */
による方法では、複数の行をまとめてコメントにできることです。たとえば、次のように使えます。
/*
徴収した参加費の合計を出力するプログラム。
登録がある会員の人数と、当日の欠席者の人数、および1人当たりの参加費のデータを使って、
実際に徴収することができた参加費の合計額を求めている。
*/
#include <iostream>
int main()
{
constexpr auto member_num = 10; // 会員の人数
constexpr auto absent_num = 1; // 欠席者の人数
constexpr auto entry_fee = 1000; // 参加費
// 徴収した参加費の合計を出力
std::cout << (member_num - absent_num) * entry_fee << "\n";
}
実行結果:
9000
ここでは、プログラムそのものの説明をソースファイルの先頭に記述するために使いました。
ところで、/*
と */
で複数の行をまとめてコメントにできるといっていますが、これは /*
を開始点として、右へみていき、行末に来たら次の行の先頭に移り、再び右へみていき・・・を繰り返し */
を見つけた時点でコメントの終わりになるという意味です。
たとえば、次のプログラムでのコメントの使い方は、期待したであろう結果にはなりません。
#include <iostream>
int main()
{ /*
constexpr auto member_num = 10; 会員の人数
constexpr auto absent_num = 1; 欠席者の人数
constexpr auto entry_fee = 1000; 参加費
*/
// 徴収した参加費の合計を出力
std::cout << (member_num - absent_num) * entry_fee << "\n";
}
これでは、日本語で書かれている部分だけでなく、constexpr ~
の部分もすべてまとめてコメントになってしまいます。
また、このようなルールであるため、/*
と */
による方法では、1つのペアの内側にほかのペアが含まれる、いわゆる入れ子(ネスト)📘の状態にすると、意図した結果になりません。
#include <iostream>
int main()
{
constexpr auto member_num = 10; // 会員の人数
constexpr auto absent_num = 1; // 欠席者の人数
constexpr auto entry_fee = 1000; // 参加費
/*
/* 徴収した参加費の合計を出力 */
std::cout << (member_num - absent_num) * entry_fee << "\n";
*/
}
この場合、最初にあらわれる /*
と */
がコメントとして扱われるため、2つ目の /*
はコメントの一部とみなされます。結果、2つ目の */
が余分な記述となります。
文字列リテラルの中に //
や /*
、*/
が現れる場合、それはコメントをあらわす記号としては扱われません。文字列を構成する文字の並びであると認識されます。
#include <iostream>
int main()
{
std::cout << "Hello, // World\n";
std::cout << "Hello/*, World*/\n";
}
実行結果:
Hello, // World
Hello/*, World*/
コメントには、後からソースコードを読む人(自分かもしれない)が、意味や意図を理解する助けになることを書くようにします。
目指すべきところは、「分かりやすくコメントを書くこと」ではなく、「コメントを書かなくても理解しやすいソースコードを書く」ことです。たとえば、変数につける名前を工夫したり、リテラルに名前を与えたりすることは、コメントに頼らずにソースコードを理解しやすくする有効な方法です。
ソースコードをみればすぐに分かることにまで、いちいちコメントを書く必要はありません。たとえば、次のプログラムのコメントは不要なものです。
#include <iostream>
int main()
{
int x {10}; // 変数 x を宣言している。初期値は 10
std::cout << x << "\n"; // 変数 x の値を出力している
}
この手のコメントが学習のときに役に立つことはあります。学習のときには、はじめて使った機能や文法がどういう意味であったかをメモしておきたい場合もあるでしょうから、このようなコメントを完全否定はしませんが、本格的な開発にまで持ち込まないほうがいいです。
コメントを書きすぎることには、次のようなデメリットがあります。
1は、ソースコードを書いたときに同時に書き添えたコメントがあるとして、あとでソースコードを修正するときに、コメントが嘘にならないようにしなければならないという話です。厄介なことに、修正したソースコードから離れた場所に書いたコメントが影響を受ける可能性もありますから、コメントを書きすぎないことにも意味があります。
たとえば、変数を宣言する行に「この変数 x は、この後の計算式で使う」とコメントを書きました。その計算式はたしかに存在しましたが、あとになって不要になったので消すことになったとします。計算式があった行と、変数を宣言している行は離れていますが、このコメントを忘れずに修正できるでしょうか?
2は、ソースコードを理解する側の負担の問題です。特に、1が怠られていたときには更なる巨大な負担を与えます。つまり、ソースコードとコメントを見比べたときに、矛盾を見つけてしまったら、読み手はその違いの理由をなんとか理解しなければならなくなるということです。コメントは実行結果に影響しないのだから、ソースコードを信じるべきなのかもしれませんが、コメントに書かれている動作を目指してつくっていたはずが、ソースコードをそのように実装できていないのかもしれません。
ソースコードの読み手は、日本語で書かれたコメントがあると、ソースコードよりもコメントを優先して読み、コメントの記述を信じ込んでしまうこともあります。
書くといいコメントの例を挙げておきます。
//
による1行コメントと、/*
と */
で範囲を指定するコメントがある/*
と */
によるコメントは入れ子にできない//
、/*
、*/
はコメントの意味にはならず、ただの文字である
新C++編の【本編】の各ページには、末尾に練習問題があります。ページ内で学んだ知識を確認する簡単な問題から、これまでに学んだ知識を組み合わせなければならない問題、あるいは更なる自力での調査や模索が必要になるような高難易度な問題をいくつか掲載しています。
問題の難易度について。
★は、すべての方が取り組める入門レベルの問題です。
★★は、自力でプログラミングができるようなるために、入門者の方であっても取り組んでほしい問題です。
★★★は、本格的にプログラマーを目指す人のための問題です。
次のプログラムの実行結果はどうなりますか?
#include <iostream>
int main()
{
int x {100};
// int x {200};
std::cout << x << "\n";
}
次のプログラムの実行結果はどうなりますか?
#include <iostream>
int main()
{
std::cout << "message1\n";
/*
std::cout << "message2\n";
std::cout << "message3\n";
std::cout << "message4\n";
*/
std::cout << "message5\n";
}
次のプログラムの実行結果はどうなりますか?
#include <iostream>
int main()
{
std::cout << "message1\n";
/*
std::cout << "message2\n";
/*
std::cout << "message3\n";
*/
std::cout << "message4\n";
*/
std::cout << "message5\n";
}
次のプログラムの実行結果はどうなりますか?
#include <iostream>
int main()
{
std::cout << "message1\n"
// << "message2\n"
<< "//message3\n"
<< "/*message4\n"
<< "message5*/\n"
<< "message6\n"
;
}
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
![]() |
管理者情報 | プライバシーポリシー |