C++ leren: polymorfisme en snijden

Beschouw het volgende voorbeeld:

#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();
}

De uitvoer is:

rawr
bark
rawr
bark

Maar ik dacht dat de output zeker “rawr bark barkbark” zou moeten zijn. Wat is er met de badDog?


Update: misschien ben je geïnteresseerd in een andere vraag van mij.


Antwoord 1, autoriteit 100%

Dit is een probleem dat ‘slicing’ wordt genoemd.

Dog()maakt een Dog-object aan. Als je Dog().makeSound()zou aanroepen, zou het “blaffen” afdrukken zoals je zou verwachten.

Het probleem is dat je de badDog, een object van het type Animal, initialiseert met deze Dog. Aangezien het Animalalleen een Animalkan bevatten en niet iets dat is afgeleid van Animal, neemt het het Animal-gedeelte van de Dogen initialiseert zichzelf daarmee.

Het type badDogis altijd Animal; het kan nooit iets anders zijn.

De enige manier waarop u polymorf gedrag in C++ kunt krijgen, is door gebruik te maken van aanwijzers (zoals u heeft aangetoond met uw voorbeeld van goodDog) of door verwijzingen te gebruiken.

Een verwijzing (bijv. Animal&) kan verwijzen naar een object van elk type afgeleid van Animalen een pointer (bijv. Animal*) kan verwijzen naar een object van elk type afgeleid van Animal. Een gewoon Animalis echter altijd een Animal, niets anders.

Sommige talen zoals Java en C# hebben referentiesemantiek, waarbij variabelen (in de meeste gevallen) slechts verwijzingen naar objecten zijn, dus gegeven een Animal rex;, is rexecht alleen een verwijzing naar een Animal, en rex = new Dog()zorgt ervoor dat rexverwijst naar een nieuw Dog-object .

C++ werkt niet op die manier: variabelen verwijzen niet naar objecten in C++, variabelen zijn objecten. Als je rex = Dog()in C++ zegt, kopieert het een nieuw Dog-object naar rex, en sinds rexis eigenlijk van het type Animal, het wordt in plakjes gesneden en alleen de Animaldelen worden gekopieerd. Dit worden waardesemantiek genoemd, wat de standaard is in C++. Als je referentiesemantiek in C++ wilt, moet je expliciet referenties of pointers gebruiken (geen van beide is hetzelfde als referenties in C# of Java, maar ze lijken meer op elkaar).


Antwoord 2, autoriteit 13%

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

Als je een Doginstantieert en deze een waarde toewijst aan een Animalvariabele, dan snijdhet object. Wat in feite betekent dat je alle Dog-ness van badDogverwijdert en in de basisklasse komt.

Om polymorfisme met basisklassen te gebruiken, moetofwel pointers ofwel verwijzingen worden gebruikt.


Antwoord 3

Je hebt badDog geïnitialiseerd met de toewijzingsoperator. Dus Dog() werd gekopieerd als Animal. De uitvoer van uw programma is correct. 🙂

Other episodes