Main Content

CERT C++: OOP51-CPP

Do not slice derived objects

Description

Rule Definition

Do not slice derived objects.1

Polyspace Implementation

The rule checker checks for Object slicing.

Examples

expand all

Issue

Object slicing 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:

  • The base class copy constructor is called.

  • 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.

Example - Function Call Causing Object Slicing
#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 //Noncompliant
    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.

Check Information

Group: 09. Object Oriented Programming (OOP)

Version History

Introduced in R2019a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.