Lesson 154 +10 XP

Object Slicing and Dynamic Cast

Object Slicing and Dynamic Cast

Two tools make inheritance safe in practice: avoiding object slicing and using dynamic_cast for safe downcasting.

Object slicing: the silent bug

If you pass a derived object by value to a function that takes the base by value, only the base part gets copied. The derived members silently disappear. That is slicing.

class Animal {
public:
  int legs;
};

class Dog : public Animal {
public:
  string bark;
};

void print(Animal a) { }   // takes the base by value

Dog rex;
print(rex);   // rex's bark member is sliced off

The function receives an object that only has the Animal members. Information is silently lost.

The fix: pass by reference

Avoid the problem by passing a reference or pointer to the base:

void print(const Animal& a) { }   // no copy, no slicing

References do not copy, so the derived object stays whole.

dynamic_cast: safe downcasting

Downcasting converts a base pointer back into a derived pointer. It is unsafe if the object is not actually that type. dynamic_cast checks at runtime and returns nullptr on failure:

Dog* d = dynamic_cast<Dog*>(animalPtr);
if (d) {
  d->bark();   // safe: animalPtr really pointed at a Dog
} else {
  cout << "Not a Dog";
}

When the pointer is not a Dog, d is nullptr, so the if statement catches the failure cleanly.

What dynamic_cast needs

The class must be polymorphic: it needs at least one virtual function (like a virtual destructor). Without that, dynamic_cast won't compile.

TL;DR

  • Slicing: a base by-value copy drops the derived part.
  • Use references or pointers to avoid copying and slicing.
  • dynamic_cast<T*>(p) safely casts down a class hierarchy.
  • It returns nullptr when the cast fails.
  • dynamic_cast requires a polymorphic class with a virtual function.