Implicit type conversion causes object slicing

C++ object passed as actual argument is sliced due to implicit type conversion.

Consider a case where there are two classes, myBase and myDerived, where myDerived is derived from myBase. myDerived contains all the data members from myBase and possibly more that are unique to myDerived. C++ allows an instance of myDerived to be assigned to an instance of myBase. Therefore, the excess data members in myDerived are "sliced away". There is a distinction between slicing and ordinary polymorphism, which is what happens when a pointer to a myDerived is assigned to a pointer to an myBase.

Slicing is considered dangerous because there is no guarantee that the author of the derived class (myDerived) will take care to maintain the data integrity of the inherited data members. In other words, not every object can be safely sliced. The example below demonstrates one way that this can happen.

C++ performs slicing implicitly at function calls. For example, when an instance of myDerived is passed to a function that expects an myBase, then myDerived is sliced. This happens because objects are passed by value. Slicing can be avoided by passing objects by reference.

ID

Observation

Description

1

Call site

The place the slicing occurred

Examples


#include <string.h>
#include <stdlib.h>
#include <stdio.h>

class myBase {
protected:
    char * m_a;
public:
    explicit myBase(char * contents) {
        int size = strlen(contents) + 1;
        m_a = (char *)malloc(size);
        strcpy_s(m_a, size, contents);
    }

    virtual ~myBase() { free(m_a); }

    virtual void print_me(void) {
       if (m_a == 0) {
           printf("I am invalid\n");
       } else {
           printf("I am %s\n", m_a);
       }
    }
};

class myDerived : public myBase {
    char m_b[10];
public:
    explicit myDerived(char * contents) : myBase(contents) {
        // myDerived caches m_a, thus making myDerived unfit to slice
        if (strlen(m_a) < 10) {
            strcpy_s(m_b, sizeof(m_b), m_a);
            free(m_a);
            m_a = 0;
        }
    }
    void print_me(void) {
        printf("I am %s\n", m_b);
    }
};

void expects_an_A(myBase parm) {
    parm.print_me(); // prints I am invalid
}

int main(int argc, char **argv)
{
    myDerived b("good");
    myBase *pa = &b;  // polymorphism, not slicing
    myBase a = b;         // b is sliced here
    pa->print_me();  // prints I am good
    b.print_me();    // prints I am good
    a.print_me();    // prints I am invalid
    expects_an_A(b); // b is also sliced here
    return 0;
}
        

Copyright © 2010, Intel Corporation. All rights reserved.