Main Content

Object slicing

Derived class object passed by value to function with base class parameter

Description

This defect occurs when you pass a derived class object by value to a function, but the function expects a base class object as parameter.

Risk

If you pass a derived class object by value to a function, you expect the derived class copy constructor to be called. If the function expects a base class object as parameter:

  1. The base class copy constructor is called.

  2. In the function body, the parameter is considered as a base class object.

In C++, virtual methods of a class are resolved at run time according to the actual type of the object. Because of object slicing, an incorrect implementation of a virtual method can be called. For instance, the base class contains a virtual method and the derived class contains an implementation of that method. When you call the virtual method from the function body, the base class method is called, even though you pass a derived class object to the function.

Fix

One possible fix is to pass the object by reference or pointer. Passing by reference or pointer does not cause invocation of copy constructors. If you do not want the object to be modified, use a const qualifier with your function parameter.

Another possible fix is to overload the function with another function that accepts the derived class object as parameter.

Examples

expand all

#include <iostream>

class Base {
public:
    explicit Base(int b) {
    	_b = b;
    }
    virtual ~Base() {} 
    virtual int update() const;
protected:
    int _b;
};


class Derived: public Base {
public:
    explicit Derived(int b):Base(b) {}
    int update() const;
};

//Class methods definition

int Base::update() const {
    return (_b + 1);
}

int Derived::update() const {
    return (_b -1);
}


//Other function definitions
void funcPassByValue(const Base bObj) {
    std::cout << "Updated _b=" << bObj.update() << std::endl;
}

int main() {
    Derived dObj(0);
    funcPassByValue(dObj);       //Function call slices object
    return 0;
 }

In this example, the call funcPassByValue(dObj) results in the output Updated _b=1 instead of the expected Updated _b=-1. Because funcPassByValue expects a Base object parameter, it calls the Base class copy constructor.

Therefore, even though you pass the Derived object dObj, the function funcPassByValue treats its parameter b as a Base object. It calls Base::update() instead of Derived::update().

Correction — Pass Object by Reference or Pointer

One possible correction is to pass the Derived object dObj by reference or by pointer. In the following, corrected example, funcPassByReference and funcPassByPointer have the same objective as funcPassByValue in the preceding example. However, funcPassByReference expects a reference to a Base object and funcPassByPointer expects a pointer to a Base object.

Passing the Derived object d by a pointer or by reference does not slice the object. The calls funcPassByReference(dObj) and funcPassByPointer(&dObj) produce the expected result Updated _b=-1.

#include <iostream>

class Base {
public:
    explicit Base(int b) {
    	_b = b;
    }
    virtual ~Base() {}
    virtual int update() const;
protected:
    int _b;
};


class Derived: public Base {
public:
    explicit Derived(int b):Base(b) {}
    int update() const;
};

//Class methods definition

int Base::update() const {
    return (_b + 1);
}

int Derived::update() const {
    return (_b -1);
}


//Other function definitions
void funcPassByReference(const Base& bRef) {
    std::cout << "Updated _b=" << bRef.update() << std::endl;
}

void funcPassByPointer(const Base* bPtr) {
    std::cout << "Updated _b=" << bPtr->update() << std::endl;
}

int main() {
    Derived dObj(0);
    funcPassByReference(dObj);       //Function call does not slice object
    funcPassByPointer(&dObj);       //Function call does not slice object
    return 0;
 }

Note

If you pass by value, because a copy of the object is made, the original object is not modified. Passing by reference or by pointer makes the object vulnerable to modification. If you are concerned about your original object being modified, add a const qualifier to your function parameter, as in the preceding example.

Result Information

Group: Object oriented
Language: C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: OBJECT_SLICING
Impact: High

Version History

Introduced in R2015b