Object Slicing
29 August, 2012 - 4 min read
When a Derived Class object is assigned to Base class, the base class' contents in the derived object are copied to the base class leaving behind the derived class specific contents. This is referred as Object Slicing. That is, the base class object can access only the base class members. This also implies the separation of base class members from derived class members has happened.
class base
{
public:
int i, j;
};
class derived : public base
{
public:
int k;
};
int main()
{
base b;
derived d;
b=d;
return 0;
}
here b contains i and j where as d contains i, j& k. On assignment only i and j of the d get copied into i and j of b. k does not get copied. on the effect object d got sliced.
In Inheritance, the attributes of the base class get carried to the derived class. However, we can assign a base class with the derived class without having the contents of the derived that are uncommon between then, copied to the base class.
class B
{
public:
int i;
};
class D : public B
{
public:
int j;
};
int main()
{
B B1;
D D1;
B1 = D1; //only i is copied to B1
}
Note:
A better term for this case might be "Partial Assignment".
Such partial assignment is not possible in Java because that language only allows object references, and assignment to a reference simply causes it to refer to a different object.
How does the compiler then call the base destructors?
The process of destructing an object takes more operations than those you write inside the body of the destructor. When the compiler generates the code for the destructor, it adds extra code both before and after the user defined code.
Before the first line of a user defined destructor is called, the compiler injects code that will make the type of the object be that of the destructor being called. That is, right before ~derived is entered, the compiler adds code that will modify the vptr to refer to the vtable of derived, so that effectively, the runtime type of the object becomes derived (*).
After the last line of your user defined code, the compiler injects calls to the member destructors as well as base destructor(s). This is performed disabling dynamic dispatch, which means that it will no longer come all the way down to the just executed destructor. It is the equivalent of adding this->~mybase(); for each base of the object (in reverse order of declaration of the bases) at the end of the destructor.
With virtual inheritance, things get a bit more complex, but overall they follow this pattern.
EDIT (forgot the (*)): (*) The standard mandates in §12/3:
When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the con- structor or destructor’s class, or overriding it in one of the other base classes of the most derived object.
That requirement implies that the runtime type of the object is that of the class being constructed/destructed at this time, even if the original object that is being constructed/destructed is of a derived type. A simple test to verify this implementation can be:
#include <iostream>
using namespace std ;
struct Base {
virtual ~Base() { f(); }
virtual void f() { std::cout << "Base" <<endl ; }
};
struct Derived : Base {
void f() { std::cout << "Derived" << endl; }
};
int main() {
Base * p = new Derived;
delete p;
return 0 ;
}
Ouput:
Base
END