web-dev-qa-db-ja.com

C ++の学習:ポリモーフィズムとスライス

次の例を考えてみましょう。

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void makeSound() {cout << "rawr" << endl;}
};

class Dog : public Animal
{
public:
    virtual void makeSound() {cout << "bark" << endl;}
};

int main()
{
    Animal animal;
    animal.makeSound();

    Dog dog;
    dog.makeSound();

    Animal badDog = Dog();
    badDog.makeSound();

    Animal* goodDog = new Dog();
    goodDog->makeSound();
}

出力は次のとおりです。

rawr
bark
rawr
bark

しかし、確かに出力は「生の樹皮樹皮樹皮」であるべきだと思いました。 badDogとは何ですか?


更新:あなたは興味があるかもしれません 私の別の質問

55
JnBrymn

これは「スライス」と呼ばれる問題です。

Dog()Dogオブジェクトを作成します。 Dog().makeSound()を呼び出すと、期待どおりに「樹皮」が出力されます。

問題は、タイプbadDogのオブジェクトであるAnimalをこのDogで初期化していることです。 AnimalにはAnimalのみを含めることができ、Animalから派生したものは含まれないため、AnimalDog部分を取り、それで初期化します。

badDogのタイプは常にAnimalです。それは決して他のものになることはできません。

C++でポリモーフィックな動作を取得する唯一の方法は、ポインターを使用するか(goodDogの例で示したように)、参照を使用することです。

参照(例:_Animal&_)はAnimalから派生した任意のタイプのオブジェクトを参照でき、ポインター(例:_Animal*_)はAnimalから派生した任意のタイプのオブジェクトを指すことができます。ただし、プレーンなAnimalは常にAnimalであり、他には何もありません。

JavaやC#などの一部の言語には参照セマンティクスがあり、変数は(ほとんどの場合)オブジェクトへの単なる参照であるため、_Animal rex;_が与えられると、rexは実際にはいくつかのAnimalへの単なる参照になります。 、およびrex = new Dog()は、rexが新しいDogオブジェクトを参照するようにします。

C++はそのようには機能しません。変数はC++のオブジェクトを参照せず、変数はオブジェクトです。 C++でrex = Dog()と言うと、新しいDogオブジェクトがrexにコピーされ、rexは実際にはタイプAnimalであるため、スライスされ、Animalパーツのみがコピーされます。これらは値セマンティクスと呼ばれ、C++のデフォルトです。 C++で参照セマンティクスが必要な場合は、参照またはポインターを明示的に使用する必要があります(これらは、C#またはJavaでの参照と同じではありませんが、より類似しています)。

73
James McNellis
 Animal badDog = Dog();
    ad.makeSound();

Dogをインスタンス化し、それを値でAnimal変数に割り当てると、 スライス オブジェクトになります。これは基本的に、DogからすべてのbadDog-nessを取り除き、それを基本クラスにすることを意味します。

基本クラスでポリモーフィズムを使用するには、ポインタまたは参照のいずれかを使用する必要があります

11
John Dibling