オーバーロードとオーバーライドはどちらも関数に関することですが、これらは似ているようで、全く異なるものです。ここでは、これについて解説します。
オーバーロードとオーバーライドを理解するには、まず、関数のシグニチャ(signature)について知る必要があります。関数のシグニチャとは、関数名と、関数の引数の型の並び方の組み合わせのことです。例えば、次の関数で考えてみましょう。
float Function(int iParam1, double dParam2) const { return iParam1 + dParam2; }
関数名と引数の部分の型の並び方、Function, int, double と、もしあれば const がシグニチャということになります。シグニチャには、関数の戻り値の型は関係ないので、上記の例では float はシグニチャの一部ではありません。
オーバーロードは、日本語で書けば、関数の多重定義です。関数のオーバーロードとはシグニチャは異なるが同じ名前の関数が複数定義されることで、例えば、次のように同じ名前の複数の関数を定義できます。
int SameName(int iValue) { return iValue; } // @ double SameName(double dValue) { return dValue; } // A float SameName(int iValue, float fValue) { return iValue + fValue; } // B void SameName(void) { } // C
関数の呼び出し時には、引数に与えられたオブジェクトの型によってどの関数が呼び出されるかが決定されます。例えば、次のようになります。
int main() { int iValue = 0; double dValue = 0.0; float fValue = 0.0f; SameName(iValue); // @が呼び出されます。 SameName(dValue); // Aが呼び出されます。 SameName(iValue, fValue); // Bが呼び出されます。 SameName(); // Cが呼び出されます。 return 0; }
演算子のオーバーロードについては、オペレータのオーバーロード をご覧ください。
関数のオーバーライドとは、日本語で書けば、関数の上書き、または、差し替えというべきものです。これは、クラスが継承されるときに、派生クラスで基本クラスのメンバ関数と同じシグニチャの関数を定義することによって行われます。通常シグニチャの同じ関数は、同じクラスのメンバ関数として定義したり、グローバル関数として定義すると二重定義エラーになってしまいますが、クラスが継承されているときには、その基本クラスと派生クラスで同じシグニチャの関数を定義できます。派生クラスを使用するときには、派生クラスのメンバ関数が優先的に使用されます。このことを、基本クラスのメンバ関数が、派生クラスのメンバ関数でオーバーロードされたと言うわけです。
class CBase { public: double GetdValue(int iValue) { return iValue; } // 基本クラスの関数 }; // 上記クラスを継承するクラス class CDerived : public CBase { public: float GetdValue(int iValue) { return iValue * 2; } // 継承クラスの関数 }; int main() { CDerived derived; double dValue1 = derived.GetdValue(2); // 継承クラスの関数が使用される。 double dValue2 = derived.CBase::GetdValue(2); // 明示すれば、ベースクラスの関数が使用できる。 printf("dValue1 = %f, dValue2 = %f\n", dValue1, dValue2); return 0; }
上記の例の結果は次のようになります。
dValue1 = 4.000000, dValue2 = 2.000000
関数のオーバーロードとオーバーライドの差を端的にまとめると次のようになります。