Let’s go back to an example we looked at previously:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class Base { protected: int m_value{}; public: Base(int value) : m_value{ value } { } virtual const char* getName() const { return "Base"; } int getValue() const { return m_value; } }; class Derived: public Base { public: Derived(int value) : Base{ value } { } virtual const char* getName() const { return "Derived"; } }; int main() { Derived derived{ 5 }; std::cout << "derived is a " << derived.getName() << " and has value " << derived.getValue() << '\n'; Base &ref{ derived }; std::cout << "ref is a " << ref.getName() << " and has value " << ref.getValue() << '\n'; Base *ptr{ &derived }; std::cout << "ptr is a " << ptr->getName() << " and has value " << ptr->getValue() << '\n'; return 0; } |
In the above example, ref references and ptr points to derived, which has a Base part, and a Derived part. Because ref and ptr are of type Base, ref and ptr can only see the Base part of derived -- the Derived part of derived still exists, but simply can’t be seen through ref or ptr. However, through use of virtual functions, we can access the most-derived version of a function. Consequently, the above program prints:
derived is a Derived and has value 5 ref is a Derived and has value 5 ptr is a Derived and has value 5
But what happens if instead of setting a Base reference or pointer to a Derived object, we simply assign a Derived object to a Base object?
1 2 3 4 5 6 7 8 |
int main() { Derived derived{ 5 }; Base base{ derived }; // what happens here? std::cout << "base is a " << base.getName() << " and has value " << base.getValue() << '\n'; return 0; } |
Remember that derived has a Base part and a Derived part. When we assign a Derived object to a Base object, only the Base portion of the Derived object is copied. The Derived portion is not. In the example above, base receives a copy of the Base portion of derived, but not the Derived portion. That Derived portion has effectively been “sliced off”. Consequently, the assigning of a Derived class object to a Base class object is called object slicing (or slicing for short).
Because variable base does not have a Derived part, base.getName() resolves to Base::getName().
The above example prints:
base is a Base and has value 5
Used conscientiously, slicing can be benign. However, used improperly, slicing can cause unexpected results in quite a few different ways. Let’s examine some of those cases.
Slicing and functions
Now, you might think the above example is a bit silly. After all, why would you assign derived to base like that? You probably wouldn’t. However, slicing is much more likely to occur accidentally with functions.
Consider the following function:
1 2 3 4 |
void printName(const Base base) // note: base passed by value, not reference { std::cout << "I am a " << base.getName() << '\n'; } |
This is a pretty simple function with a const base object parameter that is passed by value. If we call this function like such:
1 2 3 4 5 6 7 |
int main() { Derived d{ 5 }; printName(d); // oops, didn't realize this was pass by value on the calling end return 0; } |
When you wrote this program, you may not have noticed that base is a value parameter, not a reference. Therefore, when called as printName(d), we might have expected base.getName() to call virtualized function getName() and print “I am a Derived”, that is not what happens. Instead, Derived object d is sliced and only the Base portion is copied into the base parameter. When base.getName() executes, even though the getName() function is virtualized, there’s no Derived portion of the class for it to resolve to. Consequently, this program prints:
1 |
I am a Base |
In this case, it’s pretty obvious what happened, but if your functions don’t actually print any identifying information like this, tracking down the error can be challenging.
Of course, slicing here can all be easily avoided by making the function parameter a reference instead of a pass by value (yet another reason why passing classes by reference instead of value is a good idea).
1 2 3 4 5 6 7 8 9 10 11 12 |
void printName(const Base &base) // note: base now passed by reference { std::cout << "I am a " << base.getName() << '\n'; } int main() { Derived d{ 5 }; printName(d); return 0; } |
This prints:
I am a Derived
Slicing vectors
Yet another area where new programmers run into trouble with slicing is trying to implement polymorphism with std::vector. Consider the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <vector> int main() { std::vector<Base> v{}; v.push_back(Base{ 5 }); // add a Base object to our vector v.push_back(Derived{ 6 }); // add a Derived object to our vector // Print out all of the elements in our vector for (const auto& element : v) std::cout << "I am a " << element.getName() << " with value " << element.getValue() << '\n'; return 0; } |
This program compiles just fine. But when run, it prints:
I am a Base with value 5 I am a Base with value 6
Similar to the previous examples, because the std::vector was declared to be a vector of type Base, when Derived(6) was added to the vector, it was sliced.
Fixing this is a little more difficult. Many new programmers try creating a std::vector of references to an object, like this:
1 |
std::vector<Base&> v{}; |
Unfortunately, this won’t compile. The elements of std::vector must be assignable, whereas references can’t be reassigned (only initialized).
One way to address this is to make a vector of pointers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> #include <vector> int main() { std::vector<Base*> v{}; Base b{ 5 }; // b and d can't be anonymous objects Derived d{ 6 }; v.push_back(&b); // add a Base object to our vector v.push_back(&d); // add a Derived object to our vector // Print out all of the elements in our vector for (const auto* element : v) std::cout << "I am a " << element->getName() << " with value " << element->getValue() << '\n'; return 0; } |
This prints:
I am a Base with value 5 I am a Derived with value 6
which works! A few comments about this. First, nullptr is now a valid option, which may or may not be desirable. Second, you now have to deal with pointer semantics, which can be awkward. But on the upside, this also allows the possibility of dynamic memory allocation, which is useful if your objects might otherwise go out of scope.
The Frankenobject
In the above examples, we’ve seen cases where slicing lead to the wrong result because the derived class had been sliced off. Now let’s take a look at another dangerous case where the derived object still exists!
Consider the following code:
1 2 3 4 5 6 7 8 9 10 |
int main() { Derived d1{ 5 }; Derived d2{ 6 }; Base &b{ d2 }; b = d1; // this line is problematic return 0; } |
The first three lines in the function are pretty straightforward. Create two Derived objects, and set a Base reference to the second one.
The fourth line is where things go astray. Since b points at d2, and we’re assigning d1 to b, you might think that the result would be that d1 would get copied into d2 -- and it would, if b were a Derived. But b is a Base, and the operator= that C++ provides for classes isn’t virtual by default. Consequently, only the Base portion of d1 is copied into d2.
As a result, you’ll discover that d2 now has the Base portion of d1 and the Derived portion of d2. In this particular example, that’s not a problem (because the Derived class has no data of its own), but in most cases, you’ll have just created a Frankenobject -- composed of parts of multiple objects. Worse, there’s no easy way to prevent this from happening (other than avoiding assignments like this as much as possible).
Conclusion
Although C++ supports assigning derived objects to base objects via object slicing, in general, this is likely to cause nothing but headaches, and you should generally try to avoid slicing. Make sure your function parameters are references (or pointers) and try to avoid any kind of pass-by-value when it comes to derived classes.
![]() |
![]() |
![]() |
In here:
since b is a reference to d2, how is it being re-assigned object d1?
`b = d1` changes the value of `d2`, it's the same as `d2 = d1`. `b` still references `d2`.
lol. that's silly of me.
thanks for clarifying.
Adding to nascarddriver comments, Marking = operator as delete in the Base class should avoid slicing happening. Base& operator=( const Derived& d ) = delete; Now, b = d1 will giving compiling issue.
In this example wouldn't the value of *__vptr of "derived" be copied to *___vptr of "base", thus we can access the expected function ?
The vtable pointer is not copied
You can delete copy construct and assignment of polymorphic classes to avoid these problems by default.
You can then, if needed, make them virtual and use dynamic_cast.
But at least not let the compiler generate the default ones
How can the first example has a return of "Base" by having a type of char* on line 12? I mean this:
By the way why do I have to write two const keywords here?
The first `const` applies to the return type, the second `const` applies to the function. Lesson 4.13 and 8.10.
"Base" is a `const char*`, the function returns a `const char*`, all cool.
// Fix for Slicing vectors [Cpp11], reference wrapper can be used here
// OUTPUT from above code
I am a Base with value 5
I am a Derived with value 6
This line:
results in double deletion.
That was a leftover from an older version of the example, thanks for pointing it out!
In the The Frankenobject, Base reference object(b) is initialized to d2. again it is assigned to d1. It voilates the statement that "reference variables can be initialized but not assigned". correct me if I am wrong.
`b` is always referencing `d2`. `b = d1` is the same as `d2 = d1`. `b` still references `d2`, but `d2`'s value has changed.
Hello!
As much as I understand, in the following example (Frakenobject section):
on line 5, as b is a Base type reference, b is assigned only 'Base' portion of d2. Then on line 7, d1 is assigned to b(which is of type Base and had previously base part of d2) which now has the base part of d1(overwritten).
so b ends up with no derived part from either 'd1' or 'd2'..and with base part from 'd1'.
Then what does the following line mean? and how ?
"As a result, you’ll discover that d2 now has the Base portion of d1 and the Derived portion of d2."
Thanks.
Careful, `b` is a reference. It doesn't store any information, it just references another variable.
Line 5 doesn't copy anything, it initializes `b` as a reference to `d2`.
Line 7 copies the `Base` portion of `d1` into `d2` (Because `b` references `d2`).
At the very end, under "The Frankenobject"
"As a result, you’ll discover that d2 now has the Base portion of d1 and the Derived portion of d2." should be changed to "As a result, you’ll discover that b now has the Base portion of d1 and the Derived portion of d2."
b is a reference to d2, which is the actual object being modified.
Instead of using std::reference_wrapper couldn't we just use pointers like this and not worry about dynamic memory allocation? Or is there some other benefit of using reference_wrapper?
Yes, as long as your objects (b and d) remain in the same scope as your vector (so you don't end up with dangling pointers).
I gave Derived a member variable so I could test out the creation of a Frankenobject. Nice.
Hi Alex
If we pass as ref or ptr of derived class to a base , as you said base class has privilege to see all derived class data.
but still why can't we access data only present in derived class not in base class using base class pointers?
> you said base class has privilege to see all derived class data
Where did I say such a thing? A base class pointer can point to a derived object, but it can not directly see or access things in the derived part of the class (it can do so indirectly).
Derived d1(5);
Derived d2(6);
Base &b = d2;
b = d1; // this line is problematic
But b is a Base, and the operator= that C++ provides for classes isn’t virtual by default.
Is it if operator = can be overloaded for b = d1 statement, so that base part and derived part of d1 object gets copied to d2 object ?
Hi, thanks for the great tutorial. However, I don't understand why the sliced object cannot access the function of the derived class, isn't it still has a virtual table pointer that points to that function?
Good question. The sliced object is always a _copy_ of the Base portion of the original object. Because the sliced object is thus of type Base, the virtual table only points to Base functions.
Hi, thanks for replying. Is the copy of the Base portion not shallow-copied here or each of the subobject will maintain each virtual table pointer of their own? And can I change its behavior by overwriting its copy constructor or copy assignment? Sorry I am confused.
The sliced item will be shallow copied by default, and will have its own virtual table pointer. You could implement copy constructor/copy assignment to do a deep copy instead of a shallow copy, but because the object will still be an object of the base class type.
Isn't the *__vptr shallow-copied in this case? It means that it would still point to the derived class virtual table?!
(I suppose that this pointer is reset when object slicing is happening?)
"Virtual tables are set up per class, not per object, so they are not affected when you copy individual objects." - does that mean there are multiple *__vptr per object instance?.. for each class within the inheritance chain?
*__vptr won't be shallow copied. It's set on object construction based on the type of the object being constructed. There's only one __vptr per object.
I think this discussion here is great at explaining this.
https://diego.assencio.com/?index=e5f2a59886a83b5c7d2c0093dbf689f9
I think there is a typo in the following paragraphs.
"..., you might think that the result would be that d1 would get copied into d2* (must be 'b') -- and it would, if b were a Derived. But b is a Base, and the operator= that C++ provides for classes isn’t virtual by default. Consequently, only the Base portion of d1 is copied into d2* (must be 'b')."
"As a result, you’ll discover that d2* (must be 'b') now has the Base portion of d1 and the Derived portion of d2"
I think it reads more cleanly as it is. Nothing is being copied into b, as b is just a reference to d2. d2 actually holds the memory.
Yes, I was wrong. Thank you for the clarification Alex.
Typo
std::cout << "ptr is a " << ptr->getName() << " and has value " << ptr->getValue() << 'n';
'n' should be '\n'.
Hi Alex!
The first snippet is missing an include to <iostream> and all loops cause a signed/unsigned comparison warning.
Hi,
For the code you demonstrated, which is shown below. You are trying to prevent vector slicing by using dynamically allocated object and stored it as pointer within the vector. Is it possible to defined the object and have a pointer pointing toward the object beforehand and then passing into the vector without dynamically allocating it when you initiate the vector.
Hi Winston!
You _could_ do this
But @p1, @p2 and their copies inside @v will dangle as soon as @b and @d die, so don't do it.
Hi Alex,
Not sure at all, but it seems like a typo in the last example, since you were reassigned reference variable. Maybe pointer must be used instead?
Hi Pashka!
Quick example:
You can't "reassign" a reference. Once a reference is initialized, the reference is always an alias to whatever it was initialized with.
So in this case, b is an alias for d2, just with altered type information.
Hi Alex,
While copying derived object to base object we know that only base portion of the derived object gets copied to new base object, but shouldn't that also copy __vptr__ which is pointing to derived's vtable be copied as is into the new base object? if not the case, How and when is __vtpr__'s value is changed while slicing happens?
Thanks, for the great tutorial!
He said in the comments "Virtual tables are set up per class, not per object, so they are not affected when you copy individual objects.". What happens is __vptr__ is created in the Base, and inherited by the Derived classes, which utilizes the __vptr__ inherited to point to their own vtable. At this part Base have access to the __vptr__ , because it's a member of Base. But when you copy (slice the object), the derived part isn't more present, as Alex said "With a pointer or reference, you’re not making a copy -- you’re passing a way of indirectly accessing the object". Because you are slicing the object, the Base class doesn't have more access to the __vptr__ of Derived, which points to the vtable of Derived.
In resume, the Derived part because of the slicing doesn't exist in that object (I mean the Base object receiving the copy), because when doing assignment, Alex did say that there isn't a default operator= to handle it right, so it's copying only the mutual part these 2 variables have, which is the Base portion of the class.
Correct me if I'm wrong Alex.
The virtual pointer isn't a normal member variable, so it's not copied. The virtual pointer in the sliced base class is set (to the base class virtual table) when the sliced base object is created.
Alex,
I thought std::vector is supposed to contain entities of the same type, but Base and Derived are different, and so pointers to Base and Derived. Why v.push_back(Derived(6)) (or v.push_back(new Derived(6)) is allowed? Thank you.
You've always been able to assign values of one type to a vector of a different element type, so long as the value can be converted to the vector's element type.
Since Derived pointers (or references) can be converted to Base pointers (or references), this is fine -- the value is simply converted to the vector's element type and stored.
In the section "Slicing and functions" our function's parameter is const Base &base. So, as we have already learnt, the compiler won't let us call any functions that do not have the "const" keyword. I think that our function's declaration should have been like
for Base class, and
for Derived class
Good catch. I've updated the member functions to be const. Thanks!
Base base = derived;
Assuming assignment operator is called
wont derived *__vptr copied to base *__vptr?
That means *__vptr is not assigned. It is only pointer to vtable when the object is created. Since base object is created here
No. Virtual tables are set up per class, not per object, so they are not affected when you copy individual objects.
hi Alex
Thanks for the reply
Hi Alex,
Thanks a lot for your tutorials, they are tremendously helpful!
I have a question which relates to both the concept of virtual functions and the object slicing. Consider the following classes:
If I put animals of any flavour into m_animalsInStable, is there a way to call the intrinsic functions other than declaring them virtual in the Animal base class? To put it differently, within a member function of Stable can I do something like m_animalsInStable[0].get().getLitresOfMilkPerDay() ?
You have two main options:
1) Add a virtual function to the Animal base class, call that, and let it virtually resolve to the most derived class. This generally only makes sense for functions that apply to all Animals, but it's nice because you don't have to know what all the various derived classes are up-front.
2) Dynamically cast your Animal into a derived object and call whatever function you want directly. The tricky part here is that you have to have some way to determine what derived class the Animal actually is.
You _could_ create a virtual get() function that returns a pointer (or reference) to the derived object (so Cow would return a Cow* or Cow& to this or *this), but it doesn't really buy you anything that you can't do with a dynamic_cast anyway.
One way to solve #2 would be to create an AnimalType enum. Then create a virtual getAnimalType() function in Animal. Each class can self-identify by returning the proper enum. You can switch on this enum to do whatever you need to do with a derived-class-specific function. e.g.
Thanks, your answer is much appreciated!
Hey Alex,
Maybe I missed something, but can you explain why passing an object by value causes slicing and passing by reference doesn't?
When you pass an object by value, a copy of the object is made. The object's type is used to determine what to copy. Thus, if your type is a base type, only the base part will be copied.
With a pointer or reference, you're not making a copy -- you're passing a way of indirectly accessing the object. That indirection might only see the base part of the object, but the rest of the object is still there.
Ah i see, thank you
Alex, thank you for your explanation. However, I am still a bit confused.
QUOTED: "Thus, if your type is a base type, only the base part will be copied."
*__vptr is part of base type, will the *__vptr of Derived be copied to the Base object?
If yes, then the *__vptr of Base Object should point to the function of Derived?
Then why the function of Base is called eventually? Unless there is mechanism of degrading the function call to the Base Class (meaning if the *__vptr is pointing to Derived function, but as there is no Derived function in the Base object, it resolves to Base function)...?
I really wish that you could help clear up my confusion. Much appreciated!
> *__vptr is part of base type, will the *__vptr of Derived be copied to the Base object?
No. The Base class vptr is used, which only points to Base functions.
Hi alex thanks again for replying so efficiently to my questions!
for this example, std::vector creates an array of pointers which point to dynamically allocated pointers which point to dynamically allocated objects am I right? So the std::vector deals with the deallocation of dynamically allocated pointers and we have to seperately deal with dynamically allocated objects as you have done so with
am I going in the right direction?
Yes, you have it correct. There's just a typo in your sentence, "std::vector creates an array of pointers which point to dynamically allocated pointers which point to dynamically allocated objects am I right"
It should be: std::vector creates an array of pointers, each of which point to dynamically allocated objects.
You explained Franken object by below lines:
Derived d1(5);
Derived d2(6);
Base &b = d2;
b = d1;
Here in line b=d1; Base reference b is getting reassigned. Shouldn't it throw compile error ??
Thanks
References can't be reassigned. The are set upon initialization and thereafter any assignment to them is an assignment to the referenced object, not a reassignment of the reference itself.
So in the quoted example, b = d1 means "assign d1 into the object b is referencing (which is d2)". But since b is a Base, it only copies the Base potion of d1 into d2.
Hello, Alex! First off, thank you so much for these tutorials! I've been lurking for about a year. Great stuff!
My question is what happens if I use pop_back() on the vector<std::reference_wrapper<object>>?
Does it only remove the reference or does it call the object's destructor as well?
And furthermore, if the object was created using new, is there anything else I have to think about?
I tried using delete on both the the wrapper and the object (using .get()) but the compiler wouldn't let me, obviously
Cheers.
Calling pop_back on a reference wrapped element just removes the reference element from the array, it doesn't call any destructors. If the object was created using new, then you'll need to manually destroy it. But if you're creating objects using new, then you should probably be using a std::vector of pointers rather than reference_wrapper.
Alright, cool. Just wanted to know my options. Thanks a lot!
I didn't notice this before, thank the author!