2

I'm having trouble understanding how slicing occurs? for example, in this piece of code:

class A {
public:
  virtual h() {
    cout << "type A" << endl;
  }
};

class B : public A {
public:
  virtual h() override {
    cout << "type B" << endl;
  }
};

void f(A a) {
  a.h();
}

void g(A& a) {
  a.h();
}

int main() {
  A a1 = B(); // a1 is A and doesn't recognize any B properties
  A *a = new B();
  f(*a);
  g(*a);
}

I noticed that:

  1. variable a1 doens't know it's a B, but variable a does know. I refer this is happening because in variable a1 the assignment to B is by value, in contrst to variable a, where I create a pointer to B.

  2. same thing happens when I pass variable a to different functions - when I pass by value, it thinks it's an A, but when i pass by reference, it thinks it's B.

I would be happy if anyone could give me more extensive and deeper explanation. Thank you in advance!

Some programmer dude
  • 363,249
  • 31
  • 351
  • 550
E. Ginzburg
  • 145
  • 8
  • It happens when you receive instances by value instead of by reference. Solution: don't. – user207421 Sep 19 '19 at 10:40
  • 1
    Note that the whole "it thinks it's B" is because your classes are *polymorphic*. If the function `h` wasn't declared as `virtual` it would not work either, even if there's no slicing (like when using pointers or references). – Some programmer dude Sep 19 '19 at 10:42
  • 1
    As for the slicing itself, if you *copy* an object then slicing happen. For example in `A a1 = B();` you copy the (temporary) object created by `B()`, but only the `A` part of the object. When you do `A *a = new B;` you don't copy the object itself, instead you make `a` point to an actual `B` object. – Some programmer dude Sep 19 '19 at 10:44
  • The variable `a` only "knows" that it's an `A*` - a pointer to either an `A` or something derived from `A`. It is the object that `a` points to that knows that it's a `B`. – molbdnilo Sep 19 '19 at 10:44
  • Don't use `new` in C++. See [Why should C++ programmers minimize use of 'new'?](https://stackoverflow.com/q/6500313). Use a local variable instead. – L. F. Sep 19 '19 at 10:50
  • 1
    Possible duplicate of [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – L. F. Sep 19 '19 at 10:50

2 Answers2

2
  1. variable a1 doens't know it's a B

More correctly: Variable a1 was declared to be an A, so it is an A. It is not a B, and never was a B. It's not about what the variable "knows"; it's about the type of the variable. a1 was initialised from a B by "slicing" a copy of the base sub object.

I would be happy if anyone could give me more extensive and deeper explanation.

Indirection is necessary for runtime polymorphism. An object of type A is always of type A and no other type. This is simply how types are in the language. One reason for this is that the compiler must know how much memory the object needs. If the compiler reserves memory for A, then how could a potentially bigger derived instance fit in the reserved memory?

But a pointer (or reference) to A can either point at a distinct A object, or it can point at a base sub object of a class that is derived from A. It doesn't matter how big the complete object is, a pointer can point at part of the object regardless, and this doesn't affect the size of the pointer itself.

how slicing of derived classes occurs?

Slicing occurs whenever you convert a derived object to a base type. The conversion copies the base class sub object. Note that when you convert a derived value to reference to base, no slicing occurs - unless you use that reference to initialise an object of the base type for instance.

eerorika
  • 181,943
  • 10
  • 144
  • 256
1

In this statement

A a1 = B();

there is used the default copy constructor of the class A because the object a1 has the type A.

This constructor looks like

constexpr A( const A & );

So only subobject of the type A of the object of the class B is copied to the object a1. The table of pointers to virtual functions of the object a1 will contain pointer to its own virtual function.

In this declaration

A *a = new B();

the pointer a points to an object of the type B that contains the table of pointers to virtual functions that in turn contains a pointer to the virtual function of the class B.

So in this call

  g(*a);

the reference to the object of the dynamic type B is passed (the object itself was not changed when it was passed to the function by reference).

So within the function

void g(A& a) {
  a.h();
}.

there will be access to the table of virtual function pointers that contains a pointer to the virtual function of the class B. Here a new object of the class A is not created because the original object is passed by reference.

In this call

  f(*a);

the argument of the function is passed by value. That is there is a copy initialization of the parameter of the function declared like

void f(A a) {
  a.h();
}

So it is in fact the same case as in the declaration

A a1 = B();

considered above.

Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268