プログラムを構成する基本的な部品である「関数」について、C++ならではの引数の渡し方や便利な機能を学びます。他の言語で関数に慣れている方も、C++特有の概念である「参照」は特に重要なので、しっかり理解していきましょう。
プログラム内の特定のタスクを実行するコードのまとまりを「関数」と呼びます。C++では、この関数を利用する前に、コンパイラがその関数の存在と使い方を知っている必要があります。そのため、「宣言 (declaration)」と「定義 (definition)」という2つの概念が重要になります。
宣言の基本的な文法は以下の通りです。
戻り値の型 関数名(引数の型1 引数名1, 引数の型2 引数名2, ...);
int
型なら整数値を返します。()
の中に、型名 変数名
のペアをコンマで区切って記述します。引数が必要ない場合は ()
の中を空にします。;
): 宣言の最後には必ずセミコロンを付けます。関数が何も値を返す必要がない場合もあります。例えば、「画面にメッセージを表示するだけ」といった関数です。その場合、戻り値の型として void
という特別なキーワードを使います。
void printMessage(std::string message);
第2章で学んだように、int
やdouble
などの型は変数を定義するために使えましたが、void
は「型がない」ことを示す特殊な型なので、void my_variable;
のように変数を定義することはできません。あくまで関数の戻り値の型としてのみ使います。
C++のコンパイラはソースコードを上から下へと順番に読み込んでいきます。そのため、main
関数のような場所で別の関数を呼び出すコードに出会ったとき、コンパイラはその時点ですでに関数の「宣言」または「定義」を読み込んでいる必要があります。
つまり、main
関数よりも上(前)に、呼び出す関数の定義か宣言のどちらかが書かれていなければコンパイルエラーになります。
コードを整理するため、一般的にはmain
関数の前に関数の「宣言」だけを記述し、main
関数の後(または別のファイル)に具体的な処理内容である「定義」を記述するスタイルがよく使われます。
以下の例で確認してみましょう。
この例では、main
関数が始まる前にgreet
関数とadd
関数の宣言をしています。これにより、main
関数内でこれらの関数を自由な順序で呼び出すことができ、コードの可読性が向上します。関数の具体的な実装はmain
関数の後にまとめて記述することで、「プログラムの全体的な流れ(main
)」と「各部分の具体的な処理(関数の定義)」を分離して考えることができます。
C++の関数の引数の渡し方には、主に 「値渡し」「ポインタ渡し」「参照渡し」 の3つがあります。ここでは特にC++特有の「参照渡し」に注目します。
引数に渡された値がコピーされて、関数内のローカル変数として扱われます。関数内でその値を変更しても、呼び出し元の変数は影響を受けません。これは多くの言語で標準的な引数の渡し方です。
std::vector
など)を渡すと、コピーのコストが無視できなくなり、パフォーマンスが低下する可能性があります。これはC言語から引き継がれた伝統的な方法で、変数のメモリアドレスを渡します。ポインタ(アドレスを指し示す変数)を介して、呼び出し元の変数を直接変更できます。詳細は第4章で詳しく学びますが、ここでは簡単に紹介します。
ポインタは強力ですが、nullptr
(どこも指していないポインタ)の可能性を考慮する必要があるなど、扱いが少し複雑です。
C++の大きな特徴の一つが参照 (Reference) です。参照は、既存の変数に別名を付ける機能と考えることができます。。
関数に参照を渡すと、値のコピーは発生せず、関数内の引数は呼び出し元の変数の「別名」として振る舞います。そのため、関数内での操作が呼び出し元の変数に直接反映されます。構文もポインタよりずっとシンプルです。
「大きなオブジェクトを渡したいけど、コピーは避けたい。でも関数内で値を変更されたくはない」という場合に最適なのが const
参照 です。
C++のベストプラクティス:
int
やdouble
などの小さな基本型は値渡し。&
)。const
参照 (const &
) を使う。C++では、同じ名前で引数の型や個数が異なる関数を複数定義できます。これをオーバーロード (Overload) と呼びます。コンパイラは、関数呼び出し時の引数の型や個数を見て、どの関数を呼び出すべきかを自動的に判断してくれます。
これにより、printInt
, printDouble
のように別々の名前を付ける必要がなくなり、コードが直感的で読みやすくなります。
注意点として、戻り値の型が違うだけではオーバーロードはできません。あくまで引数のリストが異なる必要があります。
関数の引数に、あらかじめデフォルト値を設定しておくことができます。これにより、関数を呼び出す際に該当する引数を省略できるようになります。
デフォルト引数は、引数リストの右側から設定する必要があります。一度デフォルト引数を設定したら、それより右側にある引数もすべてデフォルト引数を持たなければなりません。
この章では、C++の関数に関する基本的ながらも重要な機能を学びました。
&
): 変数の「別名」を渡し、コピーのコストをなくします。呼び出し元の変数を変更するため、または効率化のために使います。const
参照渡し (const&
): 効率的でありながら、意図しない変更を防ぐためのC++の定石です。特に「参照」は、この先のC++プログラミングで頻繁に登場する極めて重要な概念です。値渡しとの違い、そしてconst
参照との使い分けをしっかりマスターしましょう。
2つのint
型変数の値を交換する関数 swap
を作成してください。この関数は、呼び出し元の変数の値を直接変更できるように、参照渡しを使って実装してください。
関数のオーバーロードを使い、正方形と長方形の面積を計算する calculate_area
という名前の関数を実装してください。
int side
) の場合は、正方形の面積 (side✕side) を計算して返す。int width
, int height
) の場合は、長方形の面積 (width✕height) を計算して返す。作成した関数をmain
関数から呼び出し、結果が正しく表示されることを確認してください。