関数のオーバーロードとオーバーライド

C++では、関数は同じ名前で同じ型を返すものでの引数が違うと別のものとして扱われます。

例えば、

void set(char* t){ ... }

void set(char* t1, char* t2){ ... }

void set(MyChar* c) { ... }

の3つの関数は別物です。これは普通の関数もメンバ関数も同じです。ただし、使う側からすると

あたかも同じ関数を使っているような感じになります。 このように同じ名前と返り値で引数が異なる関数を書くことを、「関数をオーバロードする」といいます。

 

前の説明で出てきたMyCharを使って説明します。

class MyChar
{
private://省略しても良い
    char* text;
    int length;
public:
    void print()
     {
        printf("text=「%s」,length=「%d」", text, length);
     }

    void
set(char* t)
    {
        text = t;
        length = strlen(text)+1;//strlenは終端記号を数えない。
   
}

    char* getText()
    {
        return text;
    }

    bool operator==(MyChar& c)
    {
        char* buf = c.getText();
        if(strcmp(text, buf)==0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }


};//クラスの定義の最後には;が必要

このクラスでは、setはchar*型しか受け付けませんがMyChar型も受け付けるようにしてみましょう。

つまりやりたいことは、

MyChar text;
text.set("hello!");
MyChar text2;
text2.set(text);

です。今のままではtext2.set(text);でエラーになります。

この拡張は簡単で、

void set(MyChar& c)
{
    set(c.getText()); //void set(char* t)の方が呼ばれる
}

を追加するだけです。もちろん派生させて追加しても良いですし、MyCharを書き換えても良いです。ここでは、MyCharを書き換えることにします。

class MyChar
{
private://省略しても良い
    char* text;
    int length;
public:
    void print()
     {
        printf("text=「%s」,length=「%d」", text, length);
     }

    void
set(char* t)
    {
        text = t;
        length = strlen(text)+1;//strlenは終端記号を数えない。
   
}

    void set(MyChar& c)//オーバーロード
    {
        set(c.getText()); //void set(char* t)の方が呼ばれる
    }

    char* getText()
    {
        return text;
    }

    bool operator==(MyChar& c)
    {
        char* buf = c.getText();
        if(strcmp(text, buf)==0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }


};//クラスの定義の最後には;が必要

うまくやりたいことが出来るようになったか実行して試してみてください。

 

メンバ関数のオーバライド

MyCharのメンバprint関数は長さも表示されて使いづらいので単純にtextの内容を表示するprintをMyChar2に再定義しましょう。 このことをオーバライドといいます。

具体的には下のようになります。

class MyChar2 : public MyChar
{
public:
    void operator=(char* c)
    {
        set(c); //MyCharの機能を引き継いでいるのでMyCharのpublicなメンバは使うことが出来る。
    }

    void print()//オーバライド
    {
        printf("%s", text);
    }
};

それをテストするmain関数です。

//プロジェクト名: override1
//MyCharとMyChar2の定義を追加すること。
int
main()
{
    MyChar MyText1;
    MyChar2 myText2;
    myText1.set("hello!");
    myText2 = "good-by!";
   
    myText1.print();
    printf("\n");
 
    myText2.print();
    printf("\n"); 

    fgetc(stdin);
    return 0;
}

どういう動きをするか実行して確かめてください。

オーバーライドは、基底クラスで既に定義されているメンバ関数を派生したクラスで再定義することです。

オーバーロードは、名前が同じで、引数が異なる関数を定義することです。

関数にはその名前、引数、そして、その他修飾子をセットにしたシグネチャという概念があります。例えば、

int getValue(int index){...}

という関数のシグネチャは、getValue, intになります。

シグネチャという概念を使い上のオーバーロードとオーバーロードの説明をし直すと以下のようになります。、

オーバーライドは、基底クラスで既に定義されいるメンバ関数と同じシグネチャを持つメンバ関数を派生したクラスで再定義することです。

オーバーロードは、シグネチャは異なるが、名前が同じ関数を定義することです。

クラスのポインタ

MyChar text1;

としたときは、メンバには

text1.getText();

のようにアクセスできましたが、ポインタの時には書き方が変わります。

MyChar* text2 = &text1;

としたときは

text2->getText();

のようにピリオドの代わりに->を使います。これは、単にそういう約束だと覚えてください。

それでは、次に進みましょう。