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?
Coming from java to c++, 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 new
ed must be delete
d, 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 delete
ing 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;
}