banner
Matrix

Matrix

Abyss
email
github

Petty C++ - Object Slicing

Object Slicing is a common polymorphism problem in C++. When a derived class object is assigned to a base class, the derived part is discarded and only the base class object part is retained, resulting in some unexpected results.

Object Slicing Example#

Consider the following code:

class Animal {
public:
    virtual string GetType() const { return "Animal"; }
    virtual string GetVoice() const { return "Voice"; }
};

class Dog : public Animal {
public:
    string GetType() const override { return "Dog"; }
    string GetVoice() const override { return "Woof"; }
};

class Cat : public Animal {
public:
    string GetType() const override { return "Cat"; }
    string GetVoice() const override { return "Miaow"; }
};

void Type(Animal& a) { cout<<a.GetType(); }
void Speak(Animal& a) { cout<<a.GetVoice(); }

int main()
{
    Dog d; Type(d); cout<<" speak "; Speak(d); cout<<" - ";
    Cat c; Type(c); cout<<" speak "; Speak(c); cout<<endl;
    return 0;
}

The correct output here is:

"Dog speak Voice - Cat speak Voice"

The declaration of the Speak function does indeed use pass-by-value, which causes object slicing. In this case, the Animal object passed to the Speak function will only retain the information of the Animal base class part, and the information of the derived class (such as the specific implementation of Dog or Cat) will be lost.

Avoiding Slicing#

This problem can be solved by changing the Speak function to accept a reference instead of the object itself:

void Speak(Animal& a) { cout << a.GetVoice(); }

With this modification, polymorphism will be correctly applied, and Speak(d) will output "Woof", while Speak(c) will output "Miaow".

Alternatively, overloads can be created, such as void Speak(Dog d) and void Speak(Cat c), which will call the correct function.

In practice, the most common approach is still to use references or pointers to preserve polymorphic behavior. The use of overloads limits the generality and extensibility of the code.

Virtual Functions and Polymorphism#

Without using virtual functions, method calls are static, meaning the function to be called is determined at compile time. In this case, the calling behavior depends on the declared type, not the actual type. In other words:

Without virtual functions, even if object slicing is avoided (e.g., by passing by reference or pointer), the methods of the derived class will not be called through a reference or pointer to the base class. When a function is called through a pointer or reference to the base class, the method of the base class will always be called, not the potentially overridden version in the derived class.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.