Search code examples
c++classpointerspolymorphismabstract-class

How to use an abstract class as the type of the index variable of a for each loop?


I was translating a Java program of mine to C++. I got to a problem trying to use polymorphism the same way as in Java.

My code looks something like this:


class Base
{
public:
    virtual void print() = 0;
};

class Derived_1 : public Base
{
public:
    void print()
    {
        std::cout << "1" << std::endl;
    }
};

class Derived_2 : public Base
{
public:
    void print()
    {
        std::cout << "2" << std::endl;
    }
};

The next are two versions of my main method that I have tried, both give me compiler errors:

1:

int main(int argc, char const *argv[])
{
    std::vector<Base> v;
    
    v.push_back(Derived_1());
    v.push_back(Derived_2());

    for(Base i: v)
    {
        i.print();
    }

    return 0;
}

Error:

object of abstract class type "Base" is not allowed:C/C++(322)
main.cpp(35, 14): function "Base::print" is a pure virtual function

2:

int main(int argc, char const *argv[])
{
    std::vector<Base*> v;
    
    v.push_back(new Derived_1());
    v.push_back(new Derived_2());

    for(Base* i: v)
    {
        i.print();
    }

    return 0;
}

Error:

expression must have class type but it has type "Base *"C/C++(153)

How would you guys solve this?


Solution

  • Coming from to , there are a lot to take-care; Especially memory management!

    In your first shown case, you are slicing the objects. See What is object slicing?

    In order to access the virtual functions, you should have used std::vector<Base*> (i.e. vector of pointer to Base), or std::vector<std::unique_ptr<Base>> or std::vector<std::shared_ptr<Base>>(i.e. vector of smart pointer to Base).

    In your second shown code, your v is a vector of pointer to Base, meaning you need to access the members via the -> operator.

    i.e. You should have accessed the print() as follows:

    i->print();
    ^^^
    

    Or alternatively:

    (*i).print();
    ^^^^^
    

    Also note that:

    • In your second case, whatever you newed must be deleted, so that the program doesn't leak memory. Alternately, we have smart memory management

    • The Base is missing a virtual destructor, which will lead to undefined behavior when deleteing the ovjects. You should add one for defined behavior. See: When to use virtual destructors?

    Using std::unique_ptr, your code will look like (example code):

    #include <memory> // std::unique_ptr, std::make_unique
    
    class Base
    {
    public:
        virtual void print() /* const */ = 0;
        // add a virtual destructor to the Base
        virtual ~Base() = default;
    };
    
    class Derived_1 : public Base
    {
    public:
        void print() override /* const */ {
            std::cout << "1" << std::endl;
        }
    };
    
    class Derived_2 : public Base
    {
    public:
        void print() override /* const */ {
            std::cout << "2" << std::endl;
        }
    };
    
    int main()
    {
        std::vector<std::unique_ptr<Base>> v;
        v.reserve(2); // for unwanted re-allocations of memory
        v.emplace_back(std::make_unique<Derived_1>());
        v.emplace_back(std::make_unique<Derived_2>());
    
        for (const auto& i : v) // auto == std::unique_ptr<Base>
        {
            i->print();
        }
        return 0;
    }