C++: C++の関数
最終更新:2026-08-31
これまでのレッスンでは、すべてのコードが
main()の中にあった。しかしプログラムが大きくなると、
main()は長く保守しにくくなる。関数がこの問題を解決——コードを小さく焦点を絞ったモジュールに分割し、それぞれが1つのタスクを担当。
1. 関数とは何か?
(1) 1.1 現実の例え
| 日常のシナリオ | プログラミングでの相当 |
|---|---|
| テレビのリモコンで「電源」ボタンを押す | turnOnTV()関数を呼び出す |
| テレビが一連のステップを実行(電源オン、チャンネル調整、画面表示) | 関数本体の中のコード |
| テレビが内部でどう動くか知る必要がない | 関数の「カプセル化」プロパティ |
関数は特定のタスクを実行する再利用可能なコードブロック。
(2) 1.2 なぜ関数が必要か?
関数なし(悪い例):
CPP
#include <iostream>
int main() {
// 最初の長方形の面積を計算
double width1 = 5.0, height1 = 3.0;
double area1 = width1 * height1;
std::cout << "面積 1: " << area1 << std::endl;
// 2番目の長方形の面積を計算
double width2 = 7.0, height2 = 2.5;
double area2 = width2 * height2;
std::cout << "面積 2: " << area2 << std::endl;
// 3番目の長方形の面積を計算
double width3 = 4.0, height3 = 6.0;
double area3 = width3 * height3;
std::cout << "面積 3: " << area3 << std::endl;
return 0;
}
関数あり(良い例):
CPP
#include <iostream>
// 関数を一度だけ定義
double calculateArea(double width, double height) {
return width * height;
}
int main() {
std::cout << "面積 1: " << calculateArea(5.0, 3.0) << std::endl;
std::cout << "面積 2: " << calculateArea(7.0, 2.5) << std::endl;
std::cout << "面積 3: " << calculateArea(4.0, 6.0) << std::endl;
return 0;
}
比較:
| 関数なし | 関数あり |
|---|---|
| コードの重複(コピペ) | コードの再利用(一度書く) |
| 複数箇所でロジックを変更 | 1つの関数本体を変更 |
| 保守が困難 | 保守が容易 |
2. 関数の定義
(1) 2.1 基本構文
TEXT
📖 参照専用
戻り型 関数名(パラメータリスト) {
// 関数本体
return 戻り値; // 戻り型がvoidなら省略可
}
(2) 2.2 関数の構成要素
| 部分 | 意味 | 例 |
|---|---|---|
| 戻り型 | 関数が返す値の型 | int、double、void(なし) |
| 関数名 | 呼び出しに使う名前(変数命名規則に従う) | calculateArea、printHello |
| パラメータリスト | 関数が受け取る入力(空でも可) | (double w, double h) |
| 関数本体 | 関数が呼ばれたときに実行されるコード | { return w * h; } |
| return | 呼び出し元に結果を返す | return area; |
▶ サンプル 1:単純な関数を定義(難易度 ⭐)
CPP
#include <iostream>
// 関数定義:2つの数の和を計算
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 5); // 関数を呼び出し
std::cout << "3 + 5 = " << result << std::endl;
return 0;
}
出力:
TEXT
📖 参照専用
3 + 5 = 8
3. 関数の呼び出し
(1) 3.1 呼び出し構文
TEXT
📖 参照専用
関数名(引数リスト);
| 用語 | 意味 | 例 |
|---|---|---|
| 関数定義 | 関数コードを書く | 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; // 関数終了、aを返す
} else {
return b; // 関数終了、bを返す
}
std::cout << "この行は実行されない" << std::endl; // デッドコード
}
int main() {
std::cout << "最大値: " << max(10, 20) << std::endl;
return 0;
}
(2) 4.2 戻り値のない関数(void)
関数が何も返す必要がない場合、voidを使う:
CPP
#include <iostream>
void printWelcome() { // voidは戻り値なしを意味
std::cout << "========== ようこそ ==========" << std::endl;
std::cout << "これはvoid関数です——戻り値は不要" << std::endl;
}
int main() {
printWelcome(); // void関数を呼び出し、値を受け取る必要はない
return 0;
}
💡 ヒント: void関数で早く抜けるには
return;(値なし)と書ける。
TEXT
📖 参照専用
void printIfPositive(int x) {
if (x <= 0) {
return; // 早く終了
}
std::cout << x << "は正です" << std::endl;
}
5. 関数プロトタイプ(前方宣言)
(1) 5.1 問題:関数は使用前に定義が必要
CPP
#include <iostream>
int main() {
std::cout << add(3, 5) << std::endl; // ❌ エラー: コンパイラはまだaddを知らない
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); // 関数プロトタイプ(セミコロンで終わる!)
int main() {
std::cout << add(3, 5) << std::endl; // ✅ コンパイラはaddが存在することを知っている
return 0;
}
int add(int a, int b) { // 関数定義
return a + b;
}
💡 ルール:
- 関数プロトタイプはセミコロンで終わる
- パラメータ名は省略可能(型だけ):
int add(int, int); - しかしパラメータ名を残すと可読性が向上
6. 練習:関数付き電卓
▶ サンプル 2:関数付き電卓を実装(難易度 ⭐⭐)
CPP
📖 参照専用
#include <iostream>
#include <iomanip>
// 関数プロトタイプ
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 << "最初の数を入力: ";
std::cin >> num1;
std::cout << "演算子を入力 (+, -, *, /): ";
std::cin >> op;
std::cout << "2番目の数を入力: ";
std::cin >> num2;
std::cout << std::fixed << std::setprecision(2);
switch (op) {
case '+':
std::cout << "結果: " << add(num1, num2) << std::endl;
break;
case '-':
std::cout << "結果: " << subtract(num1, num2) << std::endl;
break;
case '*':
std::cout << "結果: " << multiply(num1, num2) << std::endl;
break;
case '/':
if (num2 != 0) {
std::cout << "結果: " << divide(num1, num2) << std::endl;
} else {
std::cout << "エラー: ゼロ除算!" << std::endl;
}
break;
default:
std::cout << "エラー: サポートされていない演算子!" << std::endl;
break;
}
return 0;
}
// 関数定義
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 関数プロトタイプの忘れ
間違い:
TEXT
📖 参照専用
int main() {
foo(); // ❌ コンパイラはfooが何かわからない
return 0;
}
void foo() {
std::cout << "Hello" << std::endl;
}
修正: 関数プロトタイプを追加:
CPP
void foo(); // ✅ 関数プロトタイプ
int main() {
foo();
return 0;
}
void foo() {
std::cout << "Hello" << std::endl;
}
(2) 7.2 プロトタイプと定義の不一致
間違い:
TEXT
📖 参照専用
int foo(int a); // プロトタイプ:intを返す
int main() {
foo(5);
return 0;
}
double foo(int a) { // ❌ エラー:定義はdoubleを返す——不一致!
return a + 0.5;
}
(3) 7.3 return文の忘れ
間違い:
CPP
int add(int a, int b) {
int sum = a + b;
// ❌ returnを書くのを忘れた
}
int main() {
int result = add(3, 5);
std::cout << result << std::endl; // ゴミ値を出力
return 0;
}
💡 ヒント: コンパイラは
return忘れを警告しないかもしれないが、プログラムの動作は未定義——ゴミ値を出力したりクラッシュする。
▶ サンプル 3:関数のオーバーロード(難易度 ⭐⭐)
CPP
#include <iostream>
#include <string>
// オーバーロードされた関数 - 同じ名前、異なるパラメータ
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
std::string add(const std::string& a, const std::string& b) {
return a + b;
}
int main() {
std::cout << "int add: " << add(3, 5) << std::endl;
std::cout << "double add: " << add(3.14, 2.86) << std::endl;
std::cout << "string add: " << add("Hello, ", "World!") << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用int add: 8 double add: 6 string add: Hello, World!
❓ よくある質問
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より前に置いた可能性。ベストプラクティス:常にプロトタイプを先に置く。
📖 まとめ
- 関数は特定のタスクを実行する再利用可能なコードブロック
- 関数構文:
戻り型 関数名(パラメータ) { 本体 } - 関数プロトタイプ(前方宣言)はコンパイラに関数が存在することを伝える——セミコロンで終わる
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)——関数に値を渡す方法と、値渡しと参照渡しの違いを学ぶ。