user2261062
user2261062

Reputation:

Cannot access method of a child class

I made a toy example of a problem I'm facing with my code:

I have an animal which I don't know what will be after a later stage, so I initialize it to a generic animal.

But later on, I want to make it a cat, so I'm assigning myAnimal to be a Cat

#include <iostream>

class Animal {
public:
    int weight;
    virtual void Sound() {
        // To be implemented by child class
    }
};

class Cat : public Animal {
public:
    void Sound() {
        std::cout << "Miau" << std::endl;
    }

    // Only cats purr
    void Purr() {
        std::cout << "Purr" << std::endl;
    }
};

int main() {

    // At this point I don't know which animal I'll have, so I initialize it 
    // to a generic Animal
    Animal* myAnimal;
    animal->weight = 10;  


    // At this point of the code, I know what animal I want, so I assign animal 
    // to be a Cat
    double selectedAnimal = 0;      
    if (selectedAnimal == 0) {
        myAnimal = &Cat();
        // myAnimal = new Cat(); // this will just create a new Cat, losing 
        // the already assigned weight. 
        // I want to "upgrade" my generic animal, keeping its properties and adding 
        // new ones specific to cats
    }

    myAnimal->Sound();
    myAnimal->Purr();     // ERROR: Class Animal has no member Purr

    return 0;
}

I think I'm not assigning correctly myAnimal to be a Cat, but it is still an Animal. Howver the compiler doesn't complain when I do myAnimal = &Cat();.

So I don't understand if the compiler allows me to assign Animal to the class Cat myAnimal = &Cat(); why it complains when I try to use a method specific of the class Cat.

How should I reassign my generic animal in such a way that is now a full Cat with all its methods?

EDIT:

Answering some comments:

-Animal should not have a Purr method, only cats purr.

-I don't know at compile time what Animal will I have, that's why I assign it to be generic at the beginning.

I can reassign myAnimal to be a new Cat, but then any variables already set to the generic animal will be lost (eg: Animal might have a weight variable already set before knowing it's a Cat)

I'll try the suggested down-casting by @Some programmer dude

Upvotes: 0

Views: 403

Answers (3)

Tiger4Hire
Tiger4Hire

Reputation: 1091

The answer from Matthias Grün shows how to manipulate C++ to do what you want. However, my advice is to stop making C++ do what you think is right, and do it the way C++ wants to do it. C++ wants you to never throw the type of an object away. It is a strongly-typed language. It almost always an "anti-pattern" to throw away the type of an object. One common technique for avoiding this anti-pattern, is to separate "ownership" from "use". You can use a pointer to unknown-type, easily. Owning an object by a pointer to unknown type is really hard.

int main()
{
   Cat my_cat;
   Animal* any_animal = &my_cat; // non-owning pointer.
   any_animal->Sound();
   my_cat.Purr();
}

Upvotes: 1

Jan Gabriel
Jan Gabriel

Reputation: 1186

Firstly, when creating the Cat ensure that you allocate the memory correctly. Currently you are taking the address of a temporary object i.e. &Cat() which is not valid C++.

You can do this in two different ways:

// On the stack
Cat cat;
myAnimal = &cat;

// OR
myAnimal = new Cat(); // On the heap (remember to free the cat)

Then, when you want to use the animal as a cat, you can use a downcast e.g.:

auto myCatPtr = dynamic_cast<Cat*>(myAnimal);
if (myCatPtr) {
    // This means the pointer is valid
    myCatPtr->Purr();
}

Here is a working example.

Upvotes: 1

Matthias Gr&#252;n
Matthias Gr&#252;n

Reputation: 1506

All that myAnimal = &Cat(); does is create a temporary of type Cat and assign the address of it to myAnimal, leaving you with a dangling pointer. myAnimal will point to an invalid address afterwards.

Also, even if it had been correctly assigned, for example by writing

myAnimal = new Cat{};

it would still require a cast so the compiler knows that it's dealing with a Cat, for example like so:

auto pCatInstance = dynamic_cast<Cat*>(myAnimal);
if (pCatInstance != nullptr)
   pCatInstance->Purr();

Upvotes: 0

Related Questions