It is possible to use pointer or references of type Base class for derived objects as shown below:
class Shape
{
int m_sides{};
public:
Shape() = default;
Shape(int sides)
: m_sides{sides}
{
}
// to prevent object slicing
Shape(const Shape &) = delete;
Shape &operator=(const Shape &) = delete;
void about() const
{
std::cout << "Shape: ???" << '\n';
}
};
class Square : public Shape
{
public:
Square()
: Shape(4)
{
}
void about() const
{
std::cout << "Shape: Square" << '\n';
}
};
int main(int argc, char const *argv[])
{
Square s;
s.about();
Shape *s_ptr{&s};
s_ptr->about();Info
Learn about object slicing.
Running the above program gives following output:
Shape: Square
Shape: ???
The output may not be the one that we might be expecting. We might have expected the second call to call the Square’s about function.
So, when we use a pointer or a reference of type base class for a derived object, we can only access the base class members and that’s what is happening here.
One might ask why do we require to use pointer or references of base class for derived class. There can be many compelling reasons. One of them might be if we want to create a function that works with all type of shapes. We essentially can make the function take an argument of type Shape as shown below:
void doSomething(Shape* shape);We could solve the issue with function overload by creating all the function for all available shapes:
void doSomethingForSquare(Square* shape);
void doSomethingForTriangle(Triangle* shape);
void doSomethingForRombos(Rombos* shape);
//....However, creating those many functions is not a viable choice. I think you get the idea.
We could also think to use function template as shown below:
template <typename T>
void doSomething(T* shape);The issue with this approach is that T can be anything, not just Shape. We might not want doSomething to work with anything that have those interfaces but not a Shape.
This problem can be solved using virtual functions.