C++の関数
これまでのレッスンでは、すべてのコードは
main()内に記述されていました。しかし、プログラムが大きくなるにつれて、
main()は長くなり、メンテナンスが難しくなってきます。関数はこの問題を解決してくれます。関数を使えば、コードを小さく、目的が明確なモジュールに分割でき、それぞれのモジュールが1つのタスクを担当するようになります。
1. 関数とは何か?
(1) 1.1 現実世界との類推
| 日常のシナリオ | プログラミングでの対応 |
|---|---|
| テレビのリモコンの「電源」ボタンを押す | turnOnTV() 関数を呼び出す |
| テレビは一連の処理(電源投入、チャンネル合わせ、画像表示)を実行する | 関数本体内のコード |
| テレビの内部構造を知る必要はありません | 関数の「カプセル化」という特性 |
関数とは、特定のタスクを実行する、再利用可能なコードのブロックです。
(2) 1.2 なぜ関数が必要なのか?
関数を使わない場合(悪い例):
CPP
#include iostream
int main() {
// Calculate area of first rectangle
double width1 = 5.0, height1 = 3.0;
double area1 = width1 * height1;
std::cout << "Area 1: " << area1 << std::endl;
// Calculate area of second rectangle
double width2 = 7.0, height2 = 2.5;
double area2 = width2 * height2;
std::cout << "Area 2: " << area2 << std::endl;
// Calculate area of third rectangle
double width3 = 4.0, height3 = 6.0;
double area3 = width3 * height3;
std::cout << "Area 3: " << area3 << std::endl;
return 0;
}
関数を使った例(良い例):
CPP
#include iostream
// Define the function once
double calculateArea(double width, double height) {
return width * height;
}
int main() {
std::cout << "Area 1: " << calculateArea(5.0, 3.0) << std::endl;
std::cout << "Area 2: " << calculateArea(7.0, 2.5) << std::endl;
std::cout << "Area 3: " << calculateArea(4.0, 6.0) << std::endl;
return 0;
}
比較:
| 関数なし | 関数あり |
|---|---|
| コードの繰り返し(コピー&ペースト) | コードの再利用(一度書いて終わり) |
| 複数の箇所のロジックを変更する | 1つの関数本体を変更する |
| メンテナンスが難しい | メンテナンスが簡単 |
2. 関数の定義
(1) 2.1 基本的な構文
CPP
returnType functionName(parameterList) {
// Function body
return returnValue; // Omitted if return type is void
}
(2) 2.2 関数の構成要素
| パート | 意味 | 例 |
|---|---|---|
| 戻り値の型 | 関数が返す値の型 | int, double, void (なし) |
| 関数名 | 呼び出しに使用する名前(変数命名規則に従う) | calculateArea, printHello |
| パラメータ一覧 | 関数が受け取る引数(空の場合もある) | (double w, double h) |
| 関数本体 | 関数が呼び出されたときに実行されるコード | { return w * h; } |
| return | 呼び出し元に結果を返す | return area; |
(3) ▶ サンプル:1:簡単な関数の定義(難易度 ⭐)
CPP
#include iostream
// Function definition: calculate the sum of two numbers
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 5); // Call the function
std::cout << "3 + 5 = " << result << std::endl;
return 0;
}
出力:
TEXT
3 + 5 = 8
3. 関数の呼び出し
(1) 3.1 呼び出し構文
TEXT
functionName(argumentList);
| 用語 | 意味 | 例 |
|---|---|---|
| 関数の定義 | 関数のコードの記述 | int add(int a, int b) { ... } |
| 関数呼び出し | 関数の使用方法 | add(3, 5); |
| パラメータ | 関数定義内の変数 | int a, int b |
| 引数 | 呼び出し時に渡される実際の値 | 3, 5 |
💡 重要なポイント: 引数はパラメータにコピーされます(値渡し)。コピーを回避できる参照渡しについては、後で説明します。
4. 戻り値
(1) 4.1 return文
return には 2 つの機能があります:
- 結果を呼び出し元に返す
- 関数を直ちに終了します(その後に続くコードは実行されません)
CPP
#include iostream
int max(int a, int b) {
if (a > b) {
return a; // End function, return a
} else {
return b; // End function, return b
}
std::cout << "This line never runs" << std::endl; // Dead code
}
int main() {
std::cout << "Max: " << max(10, 20) << std::endl;
return 0;
}
(2) 4.2 戻り値のない関数 (void)
関数が何も返す必要がない場合は、void を使用してください:
CPP
#include iostream
void printWelcome() { // void means no return value
std::cout << "========== Welcome ==========" << std::endl;
std::cout << "This is a void function — no return needed" << std::endl;
}
int main() {
printWelcome(); // Call void function, don't need to receive a value
return 0;
}
💡 ヒント: 空の関数内で
return;(値は指定しない)と記述すると、早期に終了することができます。
CPP
void printIfPositive(int x) {
if (x <= 0) {
return; // Exit early
}
std::cout << x << " is positive" << std::endl;
}
5. 関数のプロトタイプ(前方宣言)
(1) 5.1 問題点:関数は使用前に定義しなければならない
CPP
#include iostream
int main() {
std::cout << add(3, 5) << std::endl; // ❌ Error: compiler hasn't seen add yet
return 0;
}
int add(int a, int b) {
return a + b;
}
コンパイラエラー:
TEXT
error: 'add' was not declared in this scope
(2) 5.2 解決策:関数プロトタイプ
関数のプロトタイプ(前方宣言とも呼ばれる)は、コンパイラに対して「この関数は存在します。完全な定義は後で記述されます」と伝えるものです。
CPP
#include iostream
int add(int a, int b); // Function prototype (ends with semicolon!)
int main() {
std::cout << add(3, 5) << std::endl; // ✅ Compiler knows add exists
return 0;
}
int add(int a, int b) { // Function definition
return a + b;
}
💡 ルール:
- 関数のプロトタイプは セミコロン で終わります
- パラメータ名は省略可能です(型のみ指定):
int add(int, int); - しかし、パラメータ名をそのままにしておくと可読性が向上する
6. 演習:関数付き電卓
(1) ▶ サンプル:2:関数を使った電卓を実装する(難易度 ⭐⭐)
CPP
#include iostream
#include iomanip
// Function prototypes
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
int main() {
double num1, num2;
char op;
std::cout << "Enter first number: ";
std::cin >> num1;
std::cout << "Enter operator (+, -, *, /): ";
std::cin >> op;
std::cout << "Enter second number: ";
std::cin >> num2;
std::cout << std::fixed << std::setprecision(2);
switch (op) {
case '+':
std::cout << "Result: " << add(num1, num2) << std::endl;
break;
case '-':
std::cout << "Result: " << subtract(num1, num2) << std::endl;
break;
case '*':
std::cout << "Result: " << multiply(num1, num2) << std::endl;
break;
case '/':
if (num2 != 0) {
std::cout << "Result: " << divide(num1, num2) << std::endl;
} else {
std::cout << "Error: division by zero!" << std::endl;
}
break;
default:
std::cout << "Error: unsupported operator!" << std::endl;
break;
}
return 0;
}
// Function definitions
double add(double a, double b) {
return a + b;
}
double subtract(double a, double b) {
return a - b;
}
double multiply(double a, double b) {
return a * b;
}
double divide(double a, double b) {
return a / b;
}
7. よくある間違い
(1) 7.1 関数プロトタイプの省略
誤り:
CPP
int main() {
foo(); // ❌ Compiler doesn't know what foo is
return 0;
}
void foo() {
std::cout << "Hello" << std::endl;
}
修正: 関数のプロトタイプを追加します:
CPP
void foo(); // ✅ Function prototype
int main() {
foo();
return 0;
}
void foo() {
std::cout << "Hello" << std::endl;
}
(2) 7.2 プロトタイプと定義の不一致
誤り:
CPP
int foo(int a); // Prototype: returns int
int main() {
foo(5);
return 0;
}
double foo(int a) { // ❌ Error: definition returns double — mismatch!
return a + 0.5;
}
(3) 7.3 return文の省略
誤り:
CPP
int add(int a, int b) {
int sum = a + b;
// ❌ Forgot to write return
}
int main() {
int result = add(3, 5);
std::cout << result << std::endl; // Outputs garbage value
return 0;
}
💡 ヒント: コンパイラは
return が欠落していることについて警告を出さない場合がありますが、プログラムの挙動は未定義となります。ゴミデータが出力されたり、クラッシュしたりする可能性があります。
▶ サンプル 1: functions basicsデモ (難易度 ⭐)
CPP
#include <iostream>
int main() {
// functions basicsの
std::cout << "functions basicsをびましょう!" << std::endl;
return 0;
}
❓ よくある質問
Q 関数のプロトタイプでパラメータ名を省略してもいいですか?
A はい、省略しても構いませんが、記載した方が可読性が向上します。コードが自明になるよう、
int add(int, int); ではなく int add(int a, int b); と記述してください。Q 関数は複数の値を返すことができますか?
A C++の関数は1つの戻り値を持ちます。複数の値を返すには、次の方法があります:① 参照パラメータを使用する ② 構造体やクラスを返す ③ std::tuple を使用する(C++11以降)。
Q void関数内でreturn文を書くことはできますか?
A はい!
return;(値を返さない場合)は、void関数を早期に終了させます。これは入力の検証に役立ちます。つまり、無効なデータがないかチェックし、処理を行う前にreturnで終了させることができます。Q なぜ、mainの前にプロトタイプを記述しなくても関数がコンパイルされるのですか?
A コンパイラが寛容な設定になっているか、あるいは誤って関数の定義をmainの前に記述してしまった可能性があります。ベストプラクティスとして、常にプロトタイプを先に記述するようにしましょう。
📖 まとめ
- 関数とは、特定のタスクを実行する、再利用可能なコードのブロックのことです。
- 関数の構文:
returnType functionName(parameters) { body } - 関数のプロトタイプ(前方宣言)は、その関数が存在することをコンパイラに知らせるもので、末尾にセミコロンが付きます。
returnは結果を返し、かつ関数を終了するvoidの関数は値を返さない- プロトタイプを使って関数を宣言するのは
main()の前、定義するのはその後にする
📝 練習問題
-
初心者(難易度 ⭐): 2つの整数の積を返す関数
int multiply(int a, int b)を作成してください。main()からこの関数を呼び出し、結果を出力してください。 -
中級(難易度 ⭐⭐): ある数が素数かどうかを判定する関数
bool isPrime(int n)を作成してください。main()では、ユーザーから数を入力してもらい、結果を表示してください。 -
上級(難易度 ⭐⭐⭐): 以下の関数を使ってプログラムを作成してください:
int fibonacci(int n)— n番目のフィボナッチ数を返すvoid printFibonacci(int n)— 最初の n 個のフィボナッチ数を出力します
ユーザーに n を入力してもらい、その数列と n 番目の値を表示してください。
8. 🚀 次は
関数について理解できたところで、次は関数の引数(第11課)について学びます。ここでは、関数に値を渡す方法や、値渡しと参照渡しの違いについて解説します。



