What is object slicing?

221,520

Solution 1

"Slicing" is where you assign an object of a derived class to an instance of a base class, thereby losing part of the information - some of it is "sliced" away.

For example,

class A {
   int foo;
};

class B : public A {
   int bar;
};

So an object of type B has two data members, foo and bar.

Then if you were to write this:

B b;

A a = b;

Then the information in b about member bar is lost in a.

Solution 2

Most answers here fail to explain what the actual problem with slicing is. They only explain the benign cases of slicing, not the treacherous ones. Assume, like the other answers, that you're dealing with two classes A and B, where B derives (publicly) from A.

In this situation, C++ lets you pass an instance of B to A's assignment operator (and also to the copy constructor). This works because an instance of B can be converted to a const A&, which is what assignment operators and copy-constructors expect their arguments to be.

The benign case

B b;
A a = b;

Nothing bad happens there - you asked for an instance of A which is a copy of B, and that's exactly what you get. Sure, a won't contain some of b's members, but how should it? It's an A, after all, not a B, so it hasn't even heard about these members, let alone would be able to store them.

The treacherous case

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

You might think that b2 will be a copy of b1 afterward. But, alas, it's not! If you inspect it, you'll discover that b2 is a Frankensteinian creature, made from some chunks of b1 (the chunks that B inherits from A), and some chunks of b2 (the chunks that only B contains). Ouch!

What happened? Well, C++ by default doesn't treat assignment operators as virtual. Thus, the line a_ref = b1 will call the assignment operator of A, not that of B. This is because, for non-virtual functions, the declared (formally: static) type (which is A&) determines which function is called, as opposed to the actual (formally: dynamic) type (which would be B, since a_ref references an instance of B). Now, A's assignment operator obviously knows only about the members declared in A, so it will copy only those, leaving the members added in B unchanged.

A solution

Assigning only to parts of an object usually makes little sense, yet C++, unfortunately, provides no built-in way to forbid this. You can, however, roll your own. The first step is making the assignment operator virtual. This will guarantee that it's always the actual type's assignment operator which is called, not the declared type's. The second step is to use dynamic_cast to verify that the assigned object has a compatible type. The third step is to do the actual assignment in a (protected!) member assign(), since B's assign() will probably want to use A's assign() to copy A's, members.

class A {
public:
  virtual A& operator= (const A& a) {
    assign(a);
    return *this;
  }

protected:
  void assign(const A& a) {
    // copy members of A from a to this
  }
};

class B : public A {
public:
  virtual B& operator= (const A& a) {
    if (const B* b = dynamic_cast<const B*>(&a))
      assign(*b);
    else
      throw bad_assignment();
    return *this;
  }

protected:
  void assign(const B& b) {
    A::assign(b); // Let A's assign() copy members of A from b to this
    // copy members of B from b to this
  }
};

Note that, for pure convenience, B's operator= covariantly overrides the return type, since it knows that it's returning an instance of B.

Solution 3

If You have a base class A and a derived class B, then You can do the following.

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);

Now the method wantAnA needs a copy of derived. However, the object derived cannot be copied completely, as the class B could invent additional member variables which are not in its base class A.

Therefore, to call wantAnA, the compiler will "slice off" all additional members of the derived class. The result might be an object you did not want to create, because

  • it may be incomplete,
  • it behaves like an A-object (all special behaviour of the class B is lost).

Solution 4

These are all good answers. I would just like to add an execution example when passing objects by value vs by reference:

#include <iostream>

using namespace std;

// Base class
class A {
public:
    A() {}
    A(const A& a) {
        cout << "'A' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am an 'A'" << endl; }
};

// Derived class
class B: public A {
public:
    B():A() {}
    B(const B& a):A(a) {
        cout << "'B' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am a 'B'" << endl; }
};

void g(const A & a) {
    a.run();
}

void h(const A a) {
    a.run();
}

int main() {
    cout << "Call by reference" << endl;
    g(B());
    cout << endl << "Call by copy" << endl;
    h(B());
}

The output is:

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'

Solution 5

Third match in google for "C++ slicing" gives me this Wikipedia article http://en.wikipedia.org/wiki/Object_slicing and this (heated, but the first few posts define the problem) : http://bytes.com/forum/thread163565.html

So it's when you assign an object of a subclass to the super class. The superclass knows nothing of the additional information in the subclass, and hasn't got room to store it, so the additional information gets "sliced off".

If those links don't give enough info for a "good answer" please edit your question to let us know what more you're looking for.

Share:
221,520
kayani
Author by

kayani

Updated on April 22, 2021

Comments

  • kayani
    kayani about 3 years

    Someone mentioned it in the IRC as the slicing problem.

  • foraidt
    foraidt over 15 years
    Please explain how the memory corruption can occur.
  • Marc Stober
    Marc Stober over 15 years
    Very informative, but see stackoverflow.com/questions/274626#274636 for an example of how slicing occurs during method calls (which underscores the danger a little better than the plain assignment example).
  • Walter Bright
    Walter Bright over 15 years
    I forgot that the copy ctor will reset the vptr, my mistake. But you can still get corruption if A has a pointer, and B sets that to point into B's section that gets sliced off.
  • Weeble
    Weeble over 15 years
    This problem isn't just limited to slicing. Any classes that contain pointers are going to have dubious behaviour with a default assignment operator and copy-constructor.
  • Bjarke Freund-Hansen
    Bjarke Freund-Hansen almost 15 years
    @Weeble - Which is why you override the default destructor, assignment operator and copy-constructor in these cases.
  • Arafangion
    Arafangion over 13 years
    But remember, Minok, that you're NOT passing in a reference of that object. You're passing a NEW copy of that object, but using the base class to copy it in the process.
  • Karl Bielefeldt
    Karl Bielefeldt over 13 years
    Interesting. I've been programming in C++ for 15 years and this issue never occurred to me, as I've always passed objects by reference as a matter of efficiency and personal style. Goes to show how good habits can help you.
  • Admin
    Admin almost 13 years
    @David : can you explain your line in detail Then the information in b about member bar is lost in a. . does foo data gets corrupted after assignment? but why?
  • Felix Dombek
    Felix Dombek almost 13 years
    @Hades: no data gets corrupted. It is just impossible to say a.bar because the compiler thinks that a is of type A. If you cast it back to type B then you can retrieve all hidden ("sliced away") fields.
  • Admin
    Admin almost 13 years
    @Felix Thanks but I don't think casting back (since not a pointer arithmetic) will work , A a = b; a is now object of type A which has copy of B::foo. It will be mistake to cast it back now i think.
  • Felix Dombek
    Felix Dombek almost 13 years
    @Hades: I see what you mean, I was considering pointers. You're right, assignment is not casting of course – in fact, a new object is allocated on the stack. Then bar in b is not corrupted at all, simply not copied by the compiler-generated assignment operator, so a is now a completely new object of type A with member a.foo set to the same value as b.foo.
  • Matthieu M.
    Matthieu M. almost 13 years
    @Karl: it does not help in the case of assignment. B& b = xxx; b = someDerivedClass(); still provoke slicing. It is just that usually the problem goes unnoticed.
  • curiousguy
    curiousguy over 12 years
    ""normal" object behavior" that's not "normal object behaviour", that's reference semantic. And it relates in no way with C struct, compatibility, or other non-sense the any random OOP priest told you.
  • curiousguy
    curiousguy over 12 years
    I understand the "slicing" part, but I don't understand "problem". How is it a problem that some state of dog that isn't part of class Pet (the breed data member) isn't copied in the variable pet? The code is is only interested in the Pet data members - apparently. Slicing is definitely a "problem" if it is unwanted, but I don't see that here.
  • curiousguy
    curiousguy over 12 years
    "((Dog *)ptrP)" I suggest using static_cast<Dog*>(ptrP)
  • curiousguy
    curiousguy almost 12 years
    "You can also mark the copy constructor on the base explicit" which does not help at all.
  • Dude
    Dude over 11 years
    Polymorphism requires a virtual function or CRTP (templates to make it happen at compile time)...
  • Dude
    Dude over 11 years
    protected copy/assignment on the base class and this problem is solved.
  • Dude
    Dude over 11 years
    I suggest pointing out that you will make the string 'breed' eventually leak memory without a virtual destructor (the destructor of 'string' will not be called) when deleting through 'ptrP'... Why is what you show problematic? The fix is mostly proper class design. The problem in this case is that writing down constructors to control visibility when inheriting is tedious and easily forgotten. You won't get anywhere near the danger zone with your code as there is no polymorphism involved or even mentioned (slicing will truncate your object but not make your program crash, here).
  • Dude
    Dude over 11 years
    You're right. Good practice is to use abstract base classes or to restrict the access to copy/assignment. However, it's not so easy to spot once it's there and easy to forget to take care of. Calling virtual methods with sliced *this can make mysterious things happen if you get away without an access violation.
  • Dude
    Dude over 11 years
    Another answer that explains slicing but not the problem.
  • Ankit Roy
    Ankit Roy over 11 years
    @Weeble: What makes object slicing worse than general pointer fixups is that to be certain you have prevented slicing from happening, a base class must provide converting constructors for every derived class. (Why? Any derived classes that are missed are susceptible to being picked up by the base class's copy ctor, since Derived is implicitly convertible to Base.) This is obviously counter to the Open-Closed Principle, and a big maintenance burden.
  • Alexis Pigeon
    Alexis Pigeon over 11 years
    Would you mind giving some extra details? How does your answer differ from the already posted ones?
  • looper
    looper over 11 years
    I guess that more explanation wouldn't be bad.
  • fgp
    fgp over 11 years
    This isn't "slicing", or at least a benign variant of it. The real problem occurs if you do B b1; B b2; A& b2_ref = b2; b2 = b1. You might think you have copied b1 to b2, but you haven't! You have copied a part of b1 to b2 (the part of b1 that B inherited from A), and left the other parts of b2 unchanged. b2 is now a frankensteinian creature consisting of a few bits of b1 followed by some chunks of b2. Ugh! Downvoting because I think the answer is very missleading.
  • fgp
    fgp over 11 years
    -1 This completely fails to explain the actual problem. C++ has value semantics, not reference semantics like Java, so this is all entirely to be expected. And the "fix" really is an example of truely horrible C++ code. "Fixing" non-existing problems like this type of slicing by resorting to dynamic allocation is a recipe for buggy code, leaked memory and horrible performance. Note that there are cases where slicing is bad, but this answer failes to point them out. Hint: the trouble starts if you assign through references.
  • fgp
    fgp over 11 years
    C++ is not Java! If wantAnA (as its name implies!) wants an A, then that's what it gets. And an instance of A, will, uh, behave like an A. How is that surprising?
  • fgp
    fgp over 11 years
    @curiousguy Amen, brother. It's sad to see how often C++ get bashed from not being Java, when value semantics is one of the things that makes C++ so insanely powerfull.
  • Black
    Black over 11 years
    @fgp: It's surprising, because you don't pass an A to the function.
  • fgp
    fgp over 11 years
    @Black: But wantAnA said it wants an A, so that's what it gets. It's the same as declaring a function to take an int, passing 0.1, and then complaining that the function receives 0...
  • Black
    Black about 11 years
    @fgp: The behaviour is similar. However, to the average C++ programmer it might be less obvious. As far as I understood the question, nobody is "complaining". It's just about how the compiler handles the situation. Imho, it is better to avoid slicing at all by passing (const) references.
  • fgp
    fgp about 11 years
    @Black: This, IMHO, is dangerous advice. If the value is later copied, passing by reference prevents move semantics from kicking in, because even if the value was a rvalue initially, it'll be an lvalue inside the function.
  • Thomas W
    Thomas W about 11 years
    So you'd throw out inheritance, in favour of 'move' semantics? I'd be surprised at this being considered a general "design preference", frankly.
  • Black
    Black about 11 years
    @ThomasW No, I would not throw out inheritance, but use references. If the signature of wantAnA would be void wantAnA(const A & myA), then there had been not slicing. Instead, a read-only reference to the caller's object is passed.
  • curiousguy
    curiousguy almost 11 years
    @fgp Your comment should read B b1; B b2; A& b2_ref = b2; b2_ref = b1 "The real problem occurs if you" ... derive from a class with a non-virtual assignment operator. Is A even intended for derivation? It has no virtual functions. If you derive from a type, you have to deal with the fact that its member functions can be called!
  • curiousguy
    curiousguy almost 11 years
    Then the some operations on an object of A are not allowed when the object is of type B.
  • supercat
    supercat almost 11 years
    IMHO, the problem is that there are two different kinds of substitutability that may be implied by inheritance: either any derived value may be given to code expecting a base value, or any derived reference may be used as a base reference. I would like to see a language with a type system which addresses both concepts separately. There are many cases where a derived reference should be substitutable for a base reference, but derived instances should not be substitutable for base ones; there are also many cases where instances should be convertible but references should not substitute.
  • supercat
    supercat almost 11 years
    Conceptually, in .NET, if a function returns KeyValuePair<SiameseCat, ToyotaPrius>, one should be able to store that result into a storage location of type KeyValuePair<Animal, Vehicle>, not because an instance of the former is an instance of the latter, but rather because interpreting the return value as a KVP<A,V> would effectively turn it into one. Unfortunately, that would require a separate hierarchy from the normal inheritance one, since a boxed instance of the former type is definitely not equivalent to a boxed instance of the latter.
  • Mladen B.
    Mladen B. over 10 years
    I don't understand what is so bad in your "treacherous" case. You stated that you want to: 1) get a reference to an object of class A and 2) cast the object b1 to class A and copy its stuff to a reference of the class A. What is actually wrong here is the proper logic behind the given code. In other words, you took a small image frame (A), placed it over a bigger image (B) and you painted through that frame, complaining later that your bigger image now looks ugly :) But if we just consider that framed area, it looks pretty good, just as the painter wanted, right? :)
  • fgp
    fgp over 10 years
    The problem is, differently put, that C++ by default assumes a very strong kind of substitutability - it requires the base class'es operations to workly correctly on subclass instances. And that even for operations which the compiler autogenerated like assignment. So it's not enough to not screw up your own operations in this regard, you also have to explicitly disable the wrong ones generated by the compiler. Or of course, stay away from public inheritance, which usually is a good suggestion anway ;-)
  • Praxeolitic
    Praxeolitic about 10 years
    @supercat I'm a bit unclear on the distinction you're making. Is it 1) transparently passing a derived as a substitute for a base vs 2) opaquely passing a reference to a derived as though it were in fact a reference to a base?
  • supercat
    supercat about 10 years
    In .NET, every structure type has an associated heap object type to which it is implicitly convertible. If the structure type implements an interface, methods of that interface may be invoked upon the structure "in place". If one had a structure FooStruct<T> with a field of type T implement interface IFoo<T> containing property Foo of type T that could read and write the field, then if FooStruct<Cat> inherited from FooStruct<Animal>, it should implement IFoo<Animal>, implying that one should be able to store an Animal to it.
  • supercat
    supercat about 10 years
    Storing a FooStruct<Cat> into a variable of type FooStruct<Animal> would turn it into a FooStruct<Animal>, which could hold an Animal in its field, but casting it to IFoo<Animal> would turn it into the heap object type associated with FooStruct<Cat>, which would have to implement IFoo<Animal>, but couldn't accept a non-cat Animal stored into its property.
  • user14471901
    user14471901 almost 10 years
    I recall from my C++ programming courses in university that there were standing best practices that for every class we created, we were required to write default constructors, copy constructors and assignment operators, as well as a destructor. This way you made sure that copy construction and the like happened the way you needed it to, while writing the class... rather than later on some odd behavior showing up.
  • pqnet
    pqnet almost 10 years
    the problem is mostly on the automatic casting that the compiler performs from derived to the type A. Implicit casting is always a source of unexpected behavior in C++, because it is often hard to understand from looking at the code locally that a cast took place.
  • Siyuan Ren
    Siyuan Ren almost 10 years
    Another common approach is to simply disable the copy and assignment operator. For classes within inheritance hierarchy, usually there is no reason to use value instead of reference or pointer.
  • Ark-kun
    Ark-kun over 9 years
    @supercat " if FooStruct<Cat> inherited from FooStruct<Animal>". Err, what? How can structure inherit from itself in .Net? You write some very strange things that seem to be not real.
  • supercat
    supercat over 9 years
    @Ark-kun: I was trying to indicate the problem that would occur if such a thing were allowed. If there were a means of declaring a structure type which could not be boxed "as its own type" [it could still be "boxed" by storing it into a single-element array], that would make it possible for .NET to support inheritance scenarios which are at present impeded by the fact that for every structure type, the Runtime also effectively defines a heap-object type which has the same inheritance relationships. Any relationships that can't work with heap objects, can't work with structs either.
  • fgp
    fgp over 9 years
    @Ark-kun It wouldn't inherit from itself, FooStruct<Cat> and FooStruct<Animal> aren't the same type. You can't so such a thing in Java, because the two are, as far as the runtime is concerned, the same type. But in e.g. C++, the two are completely distinct types. You should be able to use template specializations to let one specialized type (in this case FooStruct<Cat>) from another (in this case FooStruct<Animal>). Not sure about .NET, though - it does have template specialization AFAIK, but not quite as general ones as C++ has, I think.
  • paulm
    paulm over 9 years
    What the? I had no idea operators could be marked virtual
  • user1902689
    user1902689 about 9 years
    @Black, did you mean "Imho, it's better to avoid slicing at all by passing (const) pointers" rather than "references"? GCC & clang print baseClass twice... #include <iostream> using namespace std; struct baseClass { void print() const { cout << "baseClass"; } }; struct derivedClass : public baseClass { void print() const { cout << "derivedClass"; } }; void myFunction(const baseClass& theClass) { theClass.print(); } int main() { baseClass base; myFunction(base); derivedClass derived; myFunction(derived); }
  • Black
    Black about 9 years
    @user1902689 For a function to be overloaded in a derived class, you have to make it "virtual", e.g., virtual void print() const {....}
  • Ankit Roy
    Ankit Roy almost 9 years
    The "treacherous" case is indeed treacherous and I hadn't considered it before. But what you call "benign" is not benign at all -- nearly 100% of the time, it's a mistake to write this code, and the language should have been designed so that it produces a compilation error. The LSP means that a derived object should behave properly when treated as a base-class object, but "behaviour" only means "response to public method calls". The LSP permits internal state (what operator=() copies) to be arbitrarily redefined in B, so copying this to an A instance could even produce UB.
  • fgp
    fgp almost 9 years
    @j_random_hacker I don't agree. The behaviour of C++ is very sensible for a language which has complex by-value types and inheritance. I agree that it might be surprising if you come from a language in which objects always have by-reference behaviour like Java or .NET, but learning about these kinds of things is just part of learning to code effectively in C++. I don't see how A a = b could prodce UB that couldn't also happen for B b2 = b.
  • fgp
    fgp almost 9 years
    @j_random_hacker For a maybe more convincing argument as to why C++ has to prevent the benign case, consider the signature of A's assignment operator - its A& operator=(const A&). So the only way to avoid that operator being with the argument being an instance of B would be to not automatically cast instances of B to const A&. But this would interfere with the LSP...
  • Ankit Roy
    Ankit Roy almost 9 years
    If one or more data members of A are repurposed by B, UB can easily follow from A a = b. (You could say that this is bad design/implementation, but it doesn't violate the LSP provided public methods continue to "behave properly", and can often be useful.) As a concrete example, suppose A contains two int members x and y, an int* p, sets p = &x in its ctor, and requires the expression *p to be valid at all times. (Maybe other code in A sometimes does p = &y.) B adds a third int member z, and in its ctor sets p = &z. After A a = b and b's lifetime ends, a.p no longer points to anything.
  • Ankit Roy
    Ankit Roy almost 9 years
    I agree that it's a tricky problem. I think the right way would have been to make a distinction at the type level between types that grant only public access to an object's public methods (and which must therefore obey the LSP) and types that grant access to internal state. Most methods would need to take only the former as parameters (and would thereby be useable with an entire class hierarchy), while assignment operators and copy ctors would require the latter as their parameter (and not obey the LSP). But I may be overlooking things.
  • metamorphosis
    metamorphosis about 8 years
    How did you reassign a reference?
  • fgp
    fgp about 8 years
    @metamorphosis If a is a reference, a = b does not make "a" reference object "b" (like "a = &b" would, if "a" were a pointer)! It invokes the assignment operator of the object referenced by "a", which (usually) will proceed to overwrite the referenced object with the contents of object "b".
  • metamorphosis
    metamorphosis about 8 years
    Ah I see, yes, this answer explains it well too: stackoverflow.com/a/9293732/3454889
  • dfrib
    dfrib almost 8 years
    Many answers here, this one in particular, explains well what object slicing is. But given the popularity of this Q&A, it could be appropriate to also mention that one could avoid this issue altogether by making certain that non-leaf base classes should be abstract (see e.g. Item 33 in Scott Meyers _More Effective C++).
  • user2286810
    user2286810 over 7 years
    I performed a test on MSVC 2015, and at the end a_ref is exactly equal to b1. If a_ref is a frankestinian object, is hard to say, because it looks like fully legal reference to an object of type A, with b1 content.
  • fgp
    fgp over 7 years
    @user2286810 Sure, if you compare a_ref and b1 as instances of A, you won't notice anything because their A-part is indeed identical. But if you compare b1 and b2, you will notice that their A-part is identical, but whatever members are added by B still have their original values.
  • pablo_worker
    pablo_worker over 7 years
    A link with a couple of examples of how to avoid the slicing problem by using pointers or references
  • Alan W. Smith
    Alan W. Smith almost 7 years
    I recently wanted to create a status struct (StatusB) that contained statuses from struct StatusA as well as some aditional statuses. I decided to derive StatusB from StatusA, but could not come up with a way to update StatusB with data from StatusA without writing my own copy constructors. This seems like a neat solution to my problem!
  • Euri Pinhollow
    Euri Pinhollow over 6 years
    @fgp that's why you don't put B into a name of reference to A.
  • Euri Pinhollow
    Euri Pinhollow over 6 years
    "his is because for non-virtual functions, the declared type (which is A&) determines which function is called" - but only for functions which override base' virtual function and assignment operator in form T& op=(T&) is not one of them.
  • user7860670
    user7860670 over 6 years
    This answer is wrong. No information is lost here. a is capable of holding just one field and gets a copy of this field from b. It is never supposed to deal with whatever other fields b may have.
  • Adrian
    Adrian almost 6 years
    Hello. Great answer but I have one question. If I do something like this ** dev d; base* b = &d;** The slicing also takes place?
  • Croll
    Croll over 5 years
    This is one of most frustrating answers on C++ SO. This has no example, really vague and simply false: slicing normally will not cause "memory corruption" (can you please, elaborate the term?), i do not see a case where it will damage stack, or anything sensitive to execution. It can, however, be a programmer error in his own application, but it is not "corruption". Finally, most assignments like this will be done via pointer, without slicing, the pattern from OP is never discussed in most popular references.
  • Croll
    Croll over 5 years
    Do you even understand that trying to access member of type that is not defined (Dog::breed) is no way an ERROR related to SLICING?
  • Croll
    Croll over 5 years
    This is not a feature, not a quirk/misfeature. It is normal on-stack-copying behavior, since calling a function with an arg or (same) allocating stack variable of type Base must take exactly sizeof(Base) bytes in memory, with possible alignment, maybe, that's why "assignment" (on-stack-copy) will not copy derived class members, their offsets are outside sizeof. To avoid "losing data", just use pointer, like anyone else, since pointer memory is fixed in place and size, whereas stack is very volitile
  • Stefan Fabian
    Stefan Fabian over 5 years
    Downvoted because that's not a good example. It wouldn't work either if instead of copying d to b, you would use a pointer in which case d and e would still exist but Base doesn't have those members. Your example only shows that you can't access members that the class doesn't have.
  • Caleth
    Caleth about 5 years
    @pqnet it's not a cast. An A is constructed at the call site, via a call to A::A(const A &)
  • pqnet
    pqnet about 5 years
    @Caleth I was referring to the automatic conversion of const derived& into const A& that happens implicitly when you copy initialise an instance of A. My legalese wasn't up to standard 5 years ago so I used to wrote "cast" to identify all conversions, not only the explicit cast expression
  • Caleth
    Caleth about 5 years
    @pqnet there is no B&, there is no conversion, the (copy constructor's) reference (argument) is bound to the A sub-object of the B directly.
  • pqnet
    pqnet about 5 years
    @Caleth the expression derived in the expression wantAnA(derived) is an lvalue reference, typed as B& because derived is an identifier corresponding to a non-const variable of type B. The language automatically converts the reference from B& into const A& as in const_cast<const A&>(static_cast<A&>(derived)) which then allows it to invoke A(A const &) copy constructor of the A class. If such copy constructor is aware of class B and tries to infer the actual type of its argument (i.e., by trying to dynamic_cast<const B&> its argument) it can access the whole B object
  • John Z. Li
    John Z. Li about 5 years
    Definitely a misfeature of C++. Assigning a derived object to a base object should be banned, while binding a derived object to a reference or a pointer of the base class should be OK.
  • Eric
    Eric about 5 years
    @ j_random_hacker: Your example is bad - you have to implement a copy constructor manually for such an a, and the obvious implementation that makes A a1 = a2 safe also makes A a = b safe. see here
  • M.kazem Akhgary
    M.kazem Akhgary almost 5 years
    @fgp I don't expect that in OOP.
  • M.kazem Akhgary
    M.kazem Akhgary almost 5 years
    this is terrible. i have to manually fix "assign" methods if my class layout changes.
  • Vishal Sharma
    Vishal Sharma over 4 years
    @Adrian If you introduce some new member functions or member variables in the derived class then those are not accessible from the base class pointer directly. However you can still access them from inside the overloaded base class virtual functions. See this: godbolt.org/z/LABx33
  • fgp
    fgp over 4 years
    @M.kazemAkhgary Well, that's life. And it's actually not terrible, because you only have to change a class' own assign method if the class gains a new member. So it's not worse than any other class with overridden assignment or comparison operators, all of which typically must exhaustively handle all member variables.
  • Nicholas Pipitone
    Nicholas Pipitone almost 4 years
    Have to give a -1, This is a compile-time error, not a run-time error, Pet::breed does not exist.
  • user2023370
    user2023370 over 3 years
    What is the signature of the implicitly-generated copy constructor used in the declaration of A? i.e. in A a = b;?
  • user2023370
    user2023370 over 3 years
    It's A(const A&).
  • phuclv
    phuclv about 3 years
  • user904963
    user904963 over 2 years
    @KarlBielefeldt Pass by value isn't a matter of preference or only efficiency. If your function needs a value it can change freely without affecting the caller, you'll have to use pass by value.