2017-11-08 22 views
1

私はこのコード例を持っています。なぜこのように動作するのか理解したいと思います。これは、イントロのC++コースでの過去の試験論文の質問です。私は今、試験のために勉強しており、クラスの継承についての私の理解を固めようとしています。C++での継承とポインタ

#include <iostream> 
using namespace std; 

class Bird { 
    public: 
    virtual void noise() { cout << "mumble" << endl; } 
    void move() { noise(); cout << "fly" << endl; } 
}; 

class Canary: public Bird { 
    public: 
    void noise() { cout << "chirp" << endl; } 
    void move() { noise(); cout << "flap" << endl; } 
}; 

class Tweety: public Canary { 
    public: 
    void noise() { cout << "tweet" << endl; } 
    void move() { noise(); cout << "run" << endl; } 
}; 

int main() { 
    Canary *yellow = new Tweety(); 
    yellow->noise(); 
    yellow->move(); 
    return 0; 
} 

私はこのコードを実行した、出力は次のとおりです。

つぶやき つぶやき、それはノイズ()のトゥイーティー実装を呼び出している意味 フラップ

、それが呼び出していますCanary move()の実装。私はそれについて混乱している。私は多態性の考え方を理解しており、noise()は仮想的なものなので、*は黄色がTweetyへのポインタなので、Tweetyバージョンを呼び出すのが理にかなっています。しかし、それはなぜCanary版のmove()を呼びますか?

Canary *yellow = new Tweety(); 

これは*黄色はトゥイーティーのオブジェクトを指すポインタカナリア、であることを言う:

は、私は私を混乱させているもの、ラインだと思います。私は基本クラスのポインタが派生クラスを指すことができるので、私はそれで大丈夫です。しかし、黄色はTweetyを指すので、なぜTweetyの移動を使用しないのですか?

ご協力いただきありがとうございます。

+0

この例には多重継承はありません。この動作を理解しないようにするには、仮想メンバ関数と非仮想メンバ関数の違いを理解する必要があります。 –

+0

あなたは 'Canary'ポインタに' move'を呼び出し、 'Canary'には' move'は仮想ではありません。 – keith

+1

私は鳥が "不調"であるのを知らなかった –

答えて

1

move()virtualである必要があります。それ以外の場合は、ポインタ型のバージョンが呼び出されます。

6

noiseは仮想なので、呼び出すときにTweetyの実装に動的にディスパッチされます。

move仮想ではないので、コールするバージョンは、コールスルーをディスパッチしているタイプに基づいてコンパイル時に決定されます。 yellowCanaryなので、コンパイラはコンパイル時に呼び出されるものを解決し、Canarymoveメソッドを明示的に呼び出します。

+1

+1。マイナーなこと:私は "コンパイラが解決する" *を "コンパイラが解決する" *と置き換えることになります* - これはオプションではなく、必要な動作です。 – peterchen

+0

フォローアップ/明確な質問:イエローがカナリアンの場合、なぜ仮想関数の移動はカナリアの移動の実装を呼び出さないのですか?あなたはポインタ型が非仮想関数の実装を決定すると言っていますが、ポインタが指しているのは仮想関数のためのポインタの型を決めるのですか?それは、コンパイラで仮想がどのように動作するのか、つまりそれを呼び出すときの動的ディスパッチが原因です。 – Jack

+0

@Jack - 'yellow'はstatic型のCanaryです。ポインタを介して呼び出すので、多態性はあなたが呼び出した仮想関数に適用されます。'noise'は仮想関数なので' Tweety'の実装を '黄色'が指すように解決します。 'move'は仮想ではないので、静的に解決されます。つまり、コンパイラは静的な型の 'yellow'(Canary')を見て、' Canary'の 'move'関数を呼び出します。実行時に動的に解決したい場合は、 'move'仮想を作成する必要があります。 – Sean

0

ショーンとアレックスが出演しています。

さまざまなシナリオを理解するのに役立つコールケースがいくつかあります。

#include <iostream> 
using namespace std; 

class Bird { 
    public: 
    virtual void noise() { cout << "mumble" << endl; } 
    void move() { noise(); cout << "fly" << endl; } 

    void noise2() { cout << "mumble2" << endl; } 
    virtual void move2() { noise2(); cout << "fly2" << endl; } 
}; 

class Canary: public Bird { 
    public: 
    void noise() { cout << "chirp" << endl; } 
    void move() { noise(); cout << "flap" << endl; } 

    void noise2() { cout << "chirp2" << endl; } 
    void move2() { noise2(); cout << "flap2" << endl; } 
}; 

class Tweety: public Canary { 
    public: 
    void noise() { cout << "tweet" << endl; } 
    void move() { noise(); cout << "run" << endl; } 

    void noise2() { cout << "tweet2" << endl; } 
    void move2() { noise2(); cout << "run2" << endl; } 
}; 

int main() { 
    Canary *yellow = new Tweety(); 
    yellow->noise(); 
    yellow->move(); 
    yellow->noise2(); 
    yellow->move2(); 
    return 0; 
} 

/* OUTPUT: 
tweet <- virtual dispatch 
tweet <- virtual dispatch, via Bird::move() 
flap  <- direct call 
chirp2 <- direct call 
tweet2 <- direct call, from Tweety::move2() 
run2  <- virtual dispatch 
*/