0

I have the following piece of code:

#include <iostream>

class AnimalRepository {
public:
    virtual ~AnimalRepository() = default;

    virtual auto makeSound() -> void {
        std::cout << "Null" << std::endl;
    }

};

class CatRepository: public AnimalRepository {
public:
   
    auto makeSound() -> void override {
       std::cout << "Meow" << std::endl;
    }

};

class DogRepository: public AnimalRepository {
public:

    auto makeSound() -> void override {
        std::cout << "Woof" << std::endl;
    }

};

class Animal {
public:
    explicit Animal(const AnimalRepository& repository)
            : repository(repository) {
    }

    auto makeSound() -> void {
        return repository.makeSound();
    }

protected:
    AnimalRepository repository;
};

Animal cat = Animal(CatRepository());

Animal dog = Animal(DogRepository());

int main() {
    cat.makeSound();
    dog.makeSound();
};

I expected the main function to output the respective cat and dog method override functions, but instead it always returns "Null". I believe this is a case of object slicing, and I'm not quite sure I understand why the reference pointer in the Animal class constructor doesn't avoid this issue.

  • Your design seems flawed. Why should an "animal" contain a repository of different animals? Unless you'r thinking about parasites, like fleas on a dog, that makes no sense. – Some programmer dude Jan 05 '23 at 10:50
  • 2
    `auto cat = std::make_unique(Cat());` – jxh Jan 05 '23 at 10:51
  • you call `Animal::makeSound` which calls `repository::makeSound` which prints `Null`. Unclear why you expected something else. There is no object sliciing (yet?) but you need references or pointers for dynamic dispatch. But please post the real code. The code you did post is not the one producing any output because it does not compile https://godbolt.org/z/q3WYz1fKs – 463035818_is_not_an_ai Jan 05 '23 at 10:51
  • 2
    It also seems you have missed something with the whole OO and inheritance thing in your books. `Animal cat = Animal(Cat());` doesn't make sense either. – Some programmer dude Jan 05 '23 at 10:51
  • oh `Animal(Cat());` actually is object slicing – 463035818_is_not_an_ai Jan 05 '23 at 10:52
  • @Someprogrammerdude this is an changed example of what I'm trying to accomplish, only the Objects, methods, and variables are renamed. I'd rather just focus on the problem at hand than get into semantics about design, as that's already been decided for me and won't change. – spiklespacklespokle Jan 05 '23 at 10:53
  • 3
    `Animal::makeSound` is not virtual. There are many things unclear in your code. To get help you should at least post the code that compiles and produces the output you report. Read about [mcve] – 463035818_is_not_an_ai Jan 05 '23 at 10:55
  • @463035818_is_not_a_number compiles for me just fine. The only thing that's changed is the object names which should not matter. This is exactly how it's written. – spiklespacklespokle Jan 05 '23 at 10:55
  • btw `AnimalRepository::makeSound` is virtual but nothing inherits from it – 463035818_is_not_an_ai Jan 05 '23 at 10:56
  • https://godbolt.org/z/TM4jWreMf – 463035818_is_not_an_ai Jan 05 '23 at 10:57
  • 3
    You seem to have mixed up `Animal` with `AnimalRepository`. That would explain many things. – Some programmer dude Jan 05 '23 at 10:58
  • @463035818_is_not_a_number nothing has been changed except for class names. I don't know what to tell you. I gain nothing from positing a non-reproduce able example so just ignore it. Thanks – spiklespacklespokle Jan 05 '23 at 10:58
  • @Someprogrammerdude error in changing the names for this example, the issue remains. Thanks for the catch though – spiklespacklespokle Jan 05 '23 at 10:59
  • You also seem to have a very similar assignment as [this question from yesterday](https://stackoverflow.com/q/75003169/440558). – Some programmer dude Jan 05 '23 at 11:00
  • 1
    Instead of using `AnimalRepository repository` I would be using `AnimalRepository* repository` (and manually cleaning up) or `std::unique_ptr repository`. I suspect that's where the slicing is occurring – Den-Jason Jan 05 '23 at 11:04
  • 1
    When you store into `AnimalRepository repository;` there is only room for the base class, so that part is stored. The rest is lost. It has nothing to do with the parameter passing or anything else, it is just how storing things work. – BoP Jan 05 '23 at 11:04
  • @Someprogrammerdude not sure since I'm not interested in using a map, and this has a separate purpose – spiklespacklespokle Jan 05 '23 at 11:04
  • 1
    Map or vector or other container doesn't matter. And now your design makes even less sense. Remember that inheritance is an "is a" relationship. Is a "dog" really an "animal repository"? An "animal repository" is where multiple animals are stored, one way or another, not an animal in itself. I ***really*** urge you to go back to your text-books to refresh, perhaps even go to your teacher, and ask for her or him for help. – Some programmer dude Jan 05 '23 at 11:09

2 Answers2

6

The slicing is done here: AnimalRepository repository;. This can't contain a subclass because it is an instance and not a reference or pointer. It is easiest to replace this with a unique smart pointer pointing to a copy of the input.

stefaanv
  • 14,072
  • 2
  • 31
  • 53
1

Or move the whole pattern from dynamic polymorphism to (compile time) static polymorphism

#include <iostream>

class CatBehavior 
{
public:

    auto makeSound() -> void 
    {
        std::cout << "Meow" << std::endl;
    }

};

class DogBehavior 
{
public:

    auto makeSound() -> void 
    {
        std::cout << "Woof" << std::endl;
    }
};

template<typename behavior_t>
class Animal 
{
public:
    explicit Animal() 
    {
    }

    auto makeSound() -> void 
    {
        return m_behavior.makeSound();
    }

protected:
    behavior_t m_behavior;
};

int main() 
{
    Animal<CatBehavior> cat;
    Animal<DogBehavior> dog;

    cat.makeSound();
    dog.makeSound();
};
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19