関数のオーバーロード
ポリモフィズム
オブジェクト指向言語では、手続きが他言語には内重要な概念の一つに「ポリモフィズム」があります
ポリモフィズムとは、プログラムの多様性のことです
わかりやすくいうと、一つの識別子(インタフェース)で複数の実装があることをさします
この技術を使えば、ある識別子が指す参照先が複数の動作を状況に合わせて実行します
結果としてプログラムの変更部分が少なく、保守が容易になります
関数のオーバーロードとは、C++のポリモフィズムを実現する技術の一つです
C++言語では、同じ関数名で引数の型や数が異なる関数を作成することができます
それぞれ、まったく異なる関数として実装することができるため
同じような動作をする関数で、引数の型や数を柔軟に設定することができます
このような、関数名と引数を含めたものをJava言語では「シグネチャ」と呼びます
関数名が同じでも、シグネチャが異なる場合はオーバーロードできるという仕組みになっています
(当然、関数名も引数の型と数がまったく同じ場合はオーバーロードできません)
#include <iostream>
using namespace std;
void Kitty() {
cout << "Kitty on your lap\n";
}
void Kitty(char *str) {
cout << str;
}
int main() {
Kitty();
Kitty("Di Gi Gharat\n");
return 0;
}
関数 Kitty() はオーバーロードされています
何も引数を受け取らなかった場合は Kitty on your lap と表示し
char型のポインタを受け取った場合は、そのポインタを表示します
こうすれば、C言語のように似たような動作をする関数が引数の違いのために
別の名前で定義されるという面倒な事態を避けることができます
プログラマは、基本的な動作をする関数名を覚えればよいだけということになります
ただし、関数のオーバーロードは引数の型、または受け取る数が異なるというのが条件です
返す戻り値が違うというだけでは不十分なので気をつけてください
オーバーロードは、数学関数など多くの型に対応したい関数に有効です
※
初期のC++コンパイラには、オーバーロードを宣言する overload キーワードがあります
コンストラクタのオーバーロード
コンストラクタ関数もオーバーロードすることができます(デストラクタ関数はできません)
これは、実用的なクラスには欠かせないもので
これまでよりオブジェクトに合わせた柔軟な初期化が可能になります
#include <iostream>
using namespace std;
class Kitty {
public:
char *str;
Kitty() { this->str = "Kitty on your lap\n"; }
Kitty(char *str) { this->str = str; }
} obj , ary[3] = { "Di Gi Gharat\n" , "Card Captor Sakura\n" , "LOVE HINA\n" };
int main() {
cout << obj.str;
for(int i = 0 ; i < 3 ; i++) cout << ary[i].str;
return 0;
}
Kittyクラスのコンストラクタ Kitty() は、何も受け取らない場合は初期値 Kitty on your lapを
char *型のポインタを受け取った場合はそれをメンバ変数 str に格納します
このように、コンストラクタは関数の機能に柔軟性を与えますが
一般的にオーバーロードする関数は論理的に機能が共通しているものであり
また、無意味にオーバーロードせず、必要な限りのオーバーロードにするのがポイントです
曖昧なオーバーロード
オーバーロード関数を作成していれば、誰でも気づくことでしょうが
場合によっては非常に曖昧なオーバーロード関数になることがあります
関数のオーバーロードに曖昧さが残っている場合、通常はコンパイルエラーになります
例えば、次のような場合コンパイラはどちらの関数を使ってよいのか判断できません
#include<iostream>
using namespace std;
void Kitty(double x) {
cout << "double : " << x << '\n';
}
void Kitty(float x) {
cout << "float : " << x << '\n';
}
int main() {
Kitty(10);
return 0;
}
関数 Kitty() は double 型と float 型のいずれかを受け取ります
main() 関数内では、これを int 型で呼び出しているので、これは型が昇格されます
intは float にも double にも昇格できるため、コンパイラはどちらを用いればよいのか判断できません
この時、明示的にキャストすることでこのような問題を避けることができます