はじめに
C++の強力な機能の一つに、「関数のオーバーロード (overload)」があります。これは、一つのクラス(またはスコープ)の中に、同じ名前を持つ関数を複数定義することができる仕組みです。
例えば、数値を表示する print
という関数を作りたい場合、整数(int
)用、小数(double
)用、と引数の型ごとに printInt
, printDouble
のように異なる名前を付ける必要はありません。print
という一つの名前で、渡される引数の型に応じて、C++コンパイラが最適な関数を自動的に呼び分けてくれます。
この記事では、メンバ関数のオーバーロードを例に、その基本的な使い方とルールについて解説します。
関数のオーバーロードのサンプルコード
このコードは、Printer
というクラスの中に、引数の型が異なる2つの display
という名前のメンバ関数を定義(オーバーロード)しています。
完成コード
#include <iostream>
#include <string>
using namespace std;
// プリンタークラス
class Printer {
public:
// 1. int型の引数を取るdisplay関数
void display(int value);
// 2. string型の引数を取るdisplay関数
void display(string value);
};
// int型用のdisplay関数の実装
void Printer::display(int value) {
cout << "整数の値を出力します: " << value << endl;
}
// string型用のdisplay関社の実装
void Printer::display(string value) {
cout << "文字列の値を出力します: 「" << value << "」" << endl;
}
int main() {
Printer myPrinter;
// int型の値を渡すと、int型用のdisplay関数が自動で呼ばれる
myPrinter.display(12345);
// string型の値を渡すと、string型用のdisplay関数が自動で呼ばれる
myPrinter.display("こんにちは");
return 0;
}
実行結果
整数の値を出力します: 12345
文字列の値を出力します: 「こんにちは」
コードの解説とルール
シグネチャによる関数の識別
なぜ同じ名前の関数を複数定義できるのでしょうか? それは、C++コンパイラが関数を識別する際に、関数名だけでなく、**引数の「型」「個数」「順序」**も合わせて見ているからです。この「関数名+引数リスト」の組み合わせを、関数の「シグネチャ (signature)」と呼びます。
display(int value)
と display(string value)
は、関数名は同じ display
ですが、引数の型が int
と string
で異なるため、コンパイラはこれらを異なるシグネチャを持つ、別々の関数として正しく識別できます。
そして、myPrinter.display(12345);
のように関数を呼び出す際、渡された引数(12345
は int
型)に最も一致するシグネチャを持つ関数を、コンパイラが自動的に選択してくれます。
重要なルール: 戻り値の型はシグネチャに含まれない
オーバーロードで関数を区別できるのは、あくまで引数リストの違いだけです。戻り値の型が違うだけでは、同じシグネチャと見なされてしまい、オーバーロードすることはできません。
// これはコンパイルエラーになる例
class MyClass {
public:
int getValue(); // 戻り値がint
double getValue(); // 戻り値がdouble。引数リストは同じなのでNG
};
まとめ
今回は、C++の関数のオーバーロードについて解説しました。
- オーバーロードとは、同じ名前で、引数リストが異なる関数を複数定義すること。
- コンパイラは、渡された引数に最もマッチするシグネチャを持つ関数を自動で呼び出す。
- 戻り値の型の違いだけでは、オーバーロードはできない。
オーバーロードは、オブジェクト指向の三本柱の一つである**多態性(ポリモーフィズム)**を、コンパイル時に実現する静的な形の一つです。この機能を活用することで、機能ごとに別々の名前を覚える必要がなくなり、より直感的で使いやすいクラスを設計することができます。