0

How does object slicing work in c++?

Could somebody please help me understand how object slicing works under the hood. To be specific, I have class Class3 which is inheriting classes Class1 and Class2 publicly like below

class Class1 
{
    int a, b;
public:
    Class1() : a(11), b(12){}
    void get(){cout<<"\nget from Class1: this:"<<this;}
};

class Class2 
{
    int c, d;
public:
    Class2() : c(13), d(14){}
    void get(){cout<<"\nget from Class2: this:"<<this;}
};

class Class3 : public Class1, public Class2
{
public: 
    Class3(): Class1(), Class2(){};
    void get(){cout<<"\nget from Class3: this:"<<this;}
};

int main()
{
    Class3 obj;
    Class1& c1ref = obj;
    Class2& c2ref = obj;
    c1ref.get();
    c2ref.get();
    return 0;
}

Output:

get from Class1: this:0x70d12c86dd30
get from Class2: this:0x70d12c86dd38

My understanding is when we assign c1ref/c2ref with obj, it must be doing some calculations internally before it assigning value to c1ref/c2ref. How are these values calculated?

Pendyala
  • 585
  • 1
  • 6
  • 17
  • 3
    where do you see here slicing? – marcinj Mar 03 '20 at 10:06
  • 2
    You'd get slicing if you removed the `&` from `Class1& c1ref = ...` – Mat Mar 03 '20 at 10:08
  • https://stackoverflow.com/questions/21838382/how-does-c-multiple-inheritance-casting-work – Mat Mar 03 '20 at 10:11
  • https://shaharmike.com/cpp/vtable-part2/ – Hans Passant Mar 03 '20 at 10:11
  • The calculations depend on the class layout. You can take pen and paper and just draw a fictional memory layout of a fictional class. Then you'll see what calculations you need to do in your case. The standard does not define the class layout for every possible class, there is some freedom for compiler vendors. – Werner Henze Mar 03 '20 at 10:11
  • This is not slicing, it is pointer adjustment: `Class3` has two subobjects of type `Class1` and `Class2`. The addresses of these two objects are distinct. When you reference one of them, or take its pointer by implicitly converting the pointer/reference to `obj` to one of its base classes, the compiler adjusts the pointer/reference to point to the respective subobject. This happens whenever you have more than one base class. The first base class is always stored at the very beginning of the object, so the pointer adjustment has an offset of zero, i.e. is a noop. – cmaster - reinstate monica Mar 03 '20 at 10:15
  • Btw, that's why you see a difference of exactly 8 bytes in the addresses: `Class1` contains two `int`, 4 bytes each, so `sizeof(Class1) == 8`. This is the offset that is added to the address of `obj` to derive the address of the `Class2` subobject, which is stored directly after the `Class1` subobject. – cmaster - reinstate monica Mar 03 '20 at 10:17
  • These answers are really helpful. To make others get benefit from these answers, reverting my edits to original one. – Pendyala Mar 03 '20 at 10:18
  • 1
    Don't edit your question in a way that invalidate existing answers. – Jarod42 Mar 03 '20 at 10:18
  • @WernerHenze, yes, indeed. – Pendyala Mar 03 '20 at 10:31

2 Answers2

3

You example does not include object slicing per se.

In your example, Class3 is composed of Class1 and Class2. When you read pointers out you get the pointers to the subobjects (to better understand this, you could think about that it would analogue if they were member variables instead of inherited).


Object slicing occurs when you assign a subclass to a base class type (doesn't happen with pointers and references). In your example that would be equivalent of:

Class1 c1 = c3;//slicing occurs (that is, you created a brand new object of type Class1)
darune
  • 10,480
  • 2
  • 24
  • 62
1

There is no object slicing here. Let us see what your code actually does:

Class3 obj;

Ok, obj is a Class3 object. As it inherits from Class2, the implementation(*) stores a Class2 object inside it.

Class2& c2ref = obj;

You have created a reference to obj. So no slicing has occured. Simply c2ref address is the address of the Class2 subobject contained in obj (this is just an implementation detail).

Class3& c3ref = obj;

c3ref is just a reference to obj.

c2ref.get();

As get is not virtual, you call the Class2 version on obj.

c3ref.get();

You call the Class3 version on obj

To actually slice the object you should use an assignation or a copy to a Class2 object:

Class2 c2 = obj;

This is correct because obj is by inheritance a Class2 object, but c2 now only contains the Class2 members from obj and has lost the Class1 part and the specific Class3 part.


(*) C++ standard does not specify the way an implementation handles inheritance. But implementation through subobjects is commonly used, so I assumed that your implementation behaves that way.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252