What Is Object Slicing in C++?

Object slicing occurs in C++ when an instance of a derived class is assigned or passed by value to a variable of its base class type. In this process, the compiler copies only the base class portion of the derived object, effectively "slicing off" any additional member variables and specialized behaviors introduced by the derived class. This leads to unexpected program state and completely breaks runtime polymorphism.

How Slicing Happens

In C++, variables declared by value have fixed memory sizes determined at compile time. A base class instance is allocated only enough memory to hold its own member variables. If you attempt to assign an instance of a larger derived class directly to a base class variable by value, the compiler invokes the base class copy constructor or assignment operator.

Because the target variable does not possess the memory layout required to hold derived members, the compiler copies solely the subobject corresponding to the base class. All derived-specific member variables and virtual method overrides are discarded.

Consider the following example:

#include <iostream>

class Base {
public:
    virtual void print() const {
        std::cout << "I am Base\n";
    }
};

class Derived : public Base {
public:
    int extraData{42};

    void print() const override {
        std::cout << "I am Derived with data: " << extraData << "\n";
    }
};

int main() {
    Derived d;
    Base b = d; // Slicing occurs here

    b.print(); // Output: "I am Base"
    return 0;
}

In this code, b is an instance of Base, not Derived. When d is assigned to b, the base class copy constructor initializes b using only the Base part of d. As a result, the virtual table pointer within b points to Base's virtual method table, not Derived's, causing b.print() to output "I am Base".

Primary Consequences of Object Slicing

  1. Loss of Derived Member Data: Any member variables unique to the derived class are omitted from the destination object. Attempting to access them is impossible because the destination object's type is strictly the base type.
  2. Loss of Polymorphic Behavior: Virtual method dispatch depends on the dynamic type of an object. Once an object is sliced into a base instance, its dynamic type becomes the base class itself, neutralizing virtual function overrides.

How to Prevent Object Slicing

To retain polymorphic behavior and prevent slicing, avoid passing or storing derived objects by value where base types are expected:

  • Use References or Pointers: Polymorphism in C++ functions correctly when working through references (Base&) or pointers (Base*). References and pointers point to the original, fully formed derived object in memory rather than creating a truncated copy.
  • Use Smart Pointers: In modern C++, dynamic collections should hold base class pointers using smart pointers, such as std::unique_ptr<Base> or std::shared_ptr<Base>, rather than storing base class objects directly by value.
  • Make Base Classes Abstract: By introducing at least one pure virtual function (or declaring an abstract protected destructor), you prevent direct instantiation and value copying of the base class entirely, catching accidental slicing errors at compile time.
  • Delete Base Copy Operations: If a class is designed purely to be an interface or base class, explicitly deleting or protecting its copy constructor and copy assignment operator ensures that derived instances cannot be sliced into base instances.