Search code examples
c++vectorshared-ptrinsertion

I want to create a shared ptr vector of different objects


You have 2 classes Cats and Dogs and need to create a vector of shared pointers which stores the data from those 2 classes. Hints: polymorphism and keep in mind that classes can have similar fields.

So this is what I've done until now. I want to insert in that shared_ptr vector all the info Cats and Dogs classes have, but I don't know how. I only managed to insert in that vector the data from the base class.

#include <iostream>
#include <vector>
#include <memory>

class Animal
{
protected:
    int tip;
    std::string name;
    int age;
public:
    Animal(int t, std::string n, int a): tip(t), name(n), age(a) {}

    friend std::ostream& operator<<(std::ostream& os, const Animal& a)
    {
        os << "Name: " << a.name << std::endl;
        os << "Age: " << a.age << std::endl;

        return os;
    }
};

class Cats: public Animal
{
    std::string race;
    std::string pref_food;
public:
    Cats(int t = 0, std::string n = "", int a = 0, std::string r = "", std::string mnprf = ""):
        Animal(t, n, a), race(r), pref_food(mnprf) {}

    friend std::ostream& operator<<(std::ostream& os, const Cats& c)
    {
        // auto n = static_cast<Animal> (c);
        os << "Name: " << c.name << std::endl;
        os << "Age: " << c.age << std::endl;
        os << "race: " << c.race << std::endl;
        os << "Fav food: " << c.pref_food << std::endl;

        return os;
    }
};

class Dog: public Animal
{
    std::string disease;
    std::string master;
public:
    Dog(int t = 1, std::string n = "", int a = 0, std::string b = "", std::string s = "" ):
        Animal(t, n, a), disease(b), master(s) {}

    friend std::ostream& operator<<(std::ostream& os, const Dog& d)
    {
        os << "Name: " << d.name << std::endl;
        os << "Age: " << d.age << std::endl;
        os << "disease: " << d.disease << std::endl;
        os << "master: " << d.master << std::endl;

        return os;
    }
};

template<typename T>
void add(std::vector<std::shared_ptr<Animal>>& vec, const T& a)
{
    auto newptr = std::make_shared<Animal>(a);
    vec.push_back(newptr);
}

int main()
{
    std::vector<std::shared_ptr<Animal>> Animals;
    Dog d(1,"Rex", 12, "idk", "Oscar");
    Cats c(0,"Meaw", 11, "Sfinx", "Catfood");
    add(Animals,d);
    add(Animals,c);

    for(auto i: Animals)
    {
        std::cout << *i;
    }
}

Solution

  • There are a few problems with your code:

    1. Animal lacks any virtual methods. At the very least, it needs a virtual destructor, so that the destructors of Cats and Dog are called correctly when shared_ptr<Animal> calls delete on its held Animal* pointer.

    2. add() is creating an instance of Animal specifically, regardless of T. So your vector contains only real Animal objects. add() needs to create an instance of T instead. A std::shared_ptr<T> can be assigned to a std::shared_ptr<Animal> when T derives from Animal. Of course, add() is redundant, main() can just create and add the new objects directly to its vector without using add() at all.

    3. When main() calls operator<< on an Animal, it will not call the operator<< defined by Cats or Dog, only the operator<< defined by Animal. This can be fixed by having operator<< in Animal call a virtual method that Cats and Dog override. There is no need to define operator<< in derived classes when the base class also has an operator<<.

    Try this instead:

    #include <iostream>
    #include <vector>
    #include <memory>
    
    class Animal
    {
    protected:
        int tip;
        std::string name;
        int age;
    
    public:
        Animal(int t, std::string n, int a): tip(t), name(n), age(a) {}
        virtual ~Animal() {}
    
        virtual void print(std::ostream& os) const
        {
            os << "Name: " << name << std::endl;
            os << "Age: " << age << std::endl;
        }
    
        friend std::ostream& operator<<(std::ostream& os, const Animal& a)
        {
            a.print(os);
            return os;
        }
    };
    
    class Cat : public Animal
    {
        std::string race;
        std::string pref_food;
    
    public:
        Cat(int t = 0, std::string n = "", int a = 0, std::string r = "", std::string mnprf = ""):
            Animal(t, n, a), race(r), pref_food(mnprf) {}
    
        void print(std::ostream& os) const override
        {
            Animal::print(os);
            os << "race: " << race << std::endl;
            os << "Fav food: " << pref_food << std::endl;
        }
    };
    
    class Dog : public Animal
    {
        std::string disease;
        std::string master;
    
    public:
        Dog(int t = 1, std::string n = "", int a = 0, std::string b = "", std::string s = ""):
            Animal(t, n, a), disease(b), master(s) {}
    
        void print(std::ostream& os) const override
        {
            Animal::print(os);
            os << "disease: " << disease << std::endl;
            os << "master: " << master << std::endl;
        }
    };
    
    template<typename T>
    void add(std::vector<std::shared_ptr<Animal>> &vec, const T &a)
    {
        auto newptr = std::make_shared<T>(a);
        vec.push_back(newptr);
    }
    
    int main()
    {
        std::vector<std::shared_ptr<Animal>> Animals;
    
        Dog d(1,"Rex", 12, "idk", "Oscar");
        Cat c(0,"Meaw", 11, "Sfinx", "Catfood");
        add(Animals, d);
        add(Animals, c);
    
        /* alternatively:
        Animals.push_back(std::make_shared<Dog>(1,"Rex", 12, "idk", "Oscar"));
        Animals.push_back(std::make_shared<Cat>(0,"Meaw", 11, "Sfinx", "Catfood"));
        */
    
        for(auto &i: Animals)
        {
            std::cout << *i;
        }
    
        return 0;
    }
    

    Output:

    Name: Rex
    Age: 12
    disease: idk
    master: Oscar
    Name: Meaw
    Age: 11
    race: Sfinx
    Fav food: Catfood
    

    Live Demo