2011-09-21 9 views
8

誰でも次のコードの出力を説明できますか?基本クラスではconstで、派生クラスではconstではない仮想関数

#include <iostream> 
#include <string> 
class Animal 
{ 
public: 
    Animal(const std::string & name) : _name(name) { } 
    ~Animal() { } 
    virtual void printMessage() const 
    { 
     std::cout << "Hello, I'm " << _name << std::endl; 
    } 

private: 
    std::string _name; 
    // other operators and stuff 
}; 

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

int main() { 
    Cow cow("bill"); 
    Animal * animal = &cow; 
    cow.printMessage(); 
    animal->printMessage(); 
} 

出力こんにちは

で、私は法案
とMOO
こんにちはよ、私は理由を理解していない法案

です。ポインタ動物は、Cow型のオブジェクトを指します。 printMessageは仮想関数です。 Cowクラスの実装が呼び出されるのはなぜですか?

+0

+1完全で最小限の作業例 – Flexo

+0

Cowクラスの関数を 'virtual void printMessage()const'に変更しようとしましたか? –

答えて

12

Cowは、異なる署名を持つため、Animalの仮想関数を上書きしません。実際に起こっているのは、Cowであることです。の機能がAnimalにあります。

この結果はAnimalprintMessageを呼び出すと、ちょうど(それはそれをオーバーライドしていない)にかかわらず、Cowの1の、Animalでバージョンを使用しますが、Cowからそれを呼び出すと、(Cowに1を使用することですそれはAnimalから1つを隠すので)。

これを修正するにはconstAnimalに、またはconstCowに追加してください。

C++ 2011では、あなたは次のように微妙な罠を避けるためにoverrideキーワードを使用することができます:

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() override 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

お知らせprintMessage()overrideを追加しました。 printMessageが実際に基本クラスのバージョンをオーバーライドしない場合、コンパイラはエラーを発生させます。この場合、エラーが発生します。

+2

基本クラス関数がconstでなければならず、派生クラスが非constでなければならない場合は、基本関数が仮想ではない非仮想インタフェースイディオムを使用できますが、const以外のプライベート仮想関数を呼び出します。派生クラスの非const関数によってオーバーライドされます。 – derpface

+0

ありがとう、derpface、まさに私が必要なもの:) –

6

の2つの異なるバージョンがあります.1つはconstであり、1つはconstではありません。彼らは同じ名前を持っていますが、2つは無関係です。 Cowの新機能では、Animalが隠されています。そのため、コンパイラは直接呼び出すと、Cowバージョンのみを考慮します。

0

ちょうど試しました。 Cowクラスにconstを追加すると、それが動作します。

つまり、両方のクラスでvirtual void printMessage() constです。

1

それは答えを隠しピーターアレキサンダーのを理解することは私にしばらく時間がかかったが、次のようにそれを理解する別の方法は次のとおりです。

あなたは牛のクラスにメソッド名をmispelledが、それを正しく綴らと言いますAnimalクラスで:

Animal::printMessage() 
Cow::mispelledPrintMessage() 

、あなたは

Animal *animal; 
を持っている場合

あなただけ

animal->printMessage(); 

を呼び出すことができますが、mispelledPrintMessage()はAnimalクラスに存在しないため、あなたは

animal->mispelledPrintMessage(); 

を呼び出すことはできません。その新品メソッドはCowクラスであるため、ベースポインタを介して多態的に呼び出すことはできません。

したがって、Animalメソッドのシグネチャにはconstが含まれていますが、Cowメソッドではなく、派生クラスのメソッド名がやや誤っているのと似ています。

PS:もう1つの4番目の解決方法(1は両方のメソッドconstを作成する、2は両方のメソッドを非constにする、3は新しい2011 overrideキーワードを使用する)は、キャストを使用してAnimalポインタをCowポインタに強制的に適用します。

((Cow*)animal)->printMessage(); 

これは非常に醜いハックですが、私はそれをお勧めしません。

PS:私はいつも私のtoString()基底クラス内の署名とまさにこの理由のためすべての派生クラスでのconstとメソッドを作成してみてください。さらに、toString()シグネチャでconstを使用すると、const または非constオブジェクトのいずれかでtoString()を呼び出すことができます。

:このコードの

const Bad' as `this' argument of `std::string Bad::toString()' discards qualifiers 

:あなたの代わりにconstのを残して、constオブジェクトでtoString()を呼び出して渡すようにしようとした、GCCコンパイラは破棄予選エラーメッセージが表示されて文句を言うでしょう

#include <iostream> 
#include <string> 

class Bad 
{ 
public: 
     std::string toString() 
     { 
       return ""; 
     } 
}; 

int main() 
{ 
     const Bad  bad; 
     std::cout << bad.toString() << "\n"; 
     return 0; 
} 

結論として、Cowはデータメンバーを変更しないので、おそらく、派生CowクラスのprintMessage()にconstを追加することで、BASE AnimalクラスとDERIVED Cowクラスの両方がconst 。デニスさんの投稿へ

-dennisベドナー -ahd 310

1

修正。

解決策として動物(動物*)を牛*にキャストすることができます。ただし、AnimalクラスからのCow * printMessageは使用できなくなります。 したがって、((牛*)動物) - > printMessage()は、(牛*)動物でなければなりません - > misspelledPrintMessage()

0

仮想クラス内 - >不要です。あなたは、同じ署名をするためにサブクラスで機能するconstを追加するだけです

関連する問題