0

The following C++ code demonstrates what I mean in more detail.

#include <iostream>

using namespace std;

class Animal
{
    public:
        int animal_property = 20;
};

class Cat: public Animal
{
    public:
        int cat_property = 10;
};

void test(Cat c)
{
    cout << "Inside test" << endl;
}

int main()
{
    Animal hybrid = Cat();
    // cout << hybrid.cat_property << endl;    -- Breaks
    // cout << test(hybrid) << endl;           -- Breaks
    return 0;
}

In this example I have created a Cat() object, but limited its type visibility to Animal. This means I can no longer access any properties or functions on the Cat class, despite originally defining it as Cat(). I cannot also pass the object to a function which requires a Cat.

What are the benefits of doing this and how does it differ to simply comparing it to Animal animal = Animal(), are their any design patterns where this is useful?

memelord23
  • 84
  • 1
  • 2
  • 11
  • 3
    have a look at the concept of polymorphism – Daniel A. White Apr 15 '23 at 17:11
  • 3
    "but limited its type visibility to Animal" No you did not. You have actually [sliced](https://stackoverflow.com/questions/274626/what-is-object-slicing) your object. There is *no* `Cat`, as opposed to "there is a `Cat` I have no access to". See also [this](https://stackoverflow.com/questions/15188894/why-doesnt-polymorphism-work-without-pointers-references) – n. m. could be an AI Apr 15 '23 at 17:16
  • If you really want to create a cat and limit the visibility, you need to use `Cat cat; Animal& hybrid = cat;` or `Animal&& hybrid = Cat();`. Otherwise you just call the copy-constructor of `Animal` with a temporary `Cat` as argument. – fabian Apr 15 '23 at 17:45
  • `Animal hybrid = Cat();` is similar to `int x = Cat().animal_property;`. A temporary cat is created but then quickly destroyed. At the end only an object of type `Animal` survives. – chi Apr 15 '23 at 20:15
  • @DanielA.White Could I please ask for a specific link for this specific use case. Most polymorphism articles I read just describe the basic concept of a derived class behaving differently, not how slicing relates to it. Thanks. – memelord23 Apr 16 '23 at 10:19
  • Slicing is not related to polymorphism at all. Slicing is nearly universally bad. I cannot think of any legitimate use case. Unfortunately C++ allows slicing by default and with no warning. Seasoned programmers know that polymorphic classes *should be non-copyable*. No copying, no slicing. – n. m. could be an AI Apr 16 '23 at 17:22

3 Answers3

1

In this example I have created a Cat() object, but limited its type visibility to Animal.

To be more precise, you created a temporary Cat object, and then initialised an object of type Animal using the temporary. The temporary cat object no longer exists after the initialisation of the Animal variable. There was no limitation in "visibility".

What are the benefits of doing this

You're copying the Animal base object from the Cat object. If that's something you need to do, then the benefit of doing this is that you achieve your goal.

are their any design patterns where this is useful?

I don't know of any common patterns doing this. I could imagine an obscure use case of augmenting a C language struct with member functions by inheriting it in which case you can still pass the base into C functions like this. But I'm not sure if it's the ideal solution.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

This kind of definition for a variable is sometimes useful, but the real use is in function arguments like const Animal& a which is a much more common pattern.

Having function parameters of a more generic base type means these functions can be used by a wider number of derived classes.

Often these arguments are captured into properties that are, likewise, the more generic base for similar reasons.

tadman
  • 208,517
  • 23
  • 234
  • 262
0

It lets you, for example, do this:

void add_new_animal(std::vector<Animal *> &animals, AnimalSelection selection)
{
    switch (selection)
    {
    case AnimalSelection::dog:
        animals.push_back(new dog());
        break;
    case AnimalSelection::cat:
        animals.push_back(new cat());
        break;
    }
}
SRNissen
  • 480
  • 9