Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Copying a polymorphic object by value can easily result in the object being sliced. That is, only part of the information associated with the object is copied, and the remaining information is lost.

Non-Compliant Code Example

This code example is non-compliant because of the unintended data loss.

Code Block
bgColor#FFCCCC
langcpp
class Employee {
public:
  Employee(string theName) : name(theName) {};
  string getName() const {return name;}
  virtual void print() const {
    cout << "Employee: " << getName() << endl;
  }
private:
  string name;
};

class Manager : public Employee {
public:
  Manager(string theName, Employee theEmployee) :
    Employee(theName), assistant(theEmployee) {};
  Employee getAssistant() const {return assistant;}
  virtual void print() const {
    cout << "Manager: " << getName() << endl;
    cout << "Assistant: " << assistant.getName() << endl;
  }
private:
  Employee assistant;
};

int main () {
  Employee coder("Joe Smith");
  Employee typist("Bill Jones");
  Manager designer("Jane Doe", typist);

  coder = designer;  // slices Jane Doe!
  coder.print();
}

In this code, class Manager is derived from class Employee and adds additional information, namely the data member assistant. In main, the object designer of class Manager, which contains an assistant data member typist, is copied by value to the object coder of class Employee. This results in the designer object being sliced, and only the Employee information is copied. Hence, the print() statement results in the output:

Code Block
Employee: Jane Doe

The information about Jane Doe's assistant is lost.

Compliant Solution (Pointers)

Assuming exactly the same class structure as above, if pointers to the objects are used so that objects are copied by reference, then slicing does not occur.

Code Block
bgColor#ccccff
langcpp
int main () {
  Employee *coder = new Employee("Joe Smith");
  Employee *typist = new Employee("Bill Jones");
  Manager *designer = new Manager("Jane Doe", *typist);

  coder = designer;
  coder->print();
}

Now, the object designer is not sliced, and the output is:

Code Block
Manager: Jane Doe
Assistant: Bill Jones

Compliant Solution (Smart Pointers)

Alternatively, it is often safer to use a smart pointer, like std::auto_ptr, to hold the address of allocated memory. This is typically more robust than the use of raw pointers.

Code Block
bgColor#ccccff
langcpp
int main () {
  auto_ptr<Employee> coder( new Employee("Joe Smith") );
  auto_ptr<Employee> typist( new Employee("Bill Jones") );
  auto_ptr<Manager> designer( new Manager("Jane Doe", *typist) );

  coder = designer; // Smith deleted, Doe xferred
  coder->print();
  // everyone deleted
}

Compliant Solution (References)

Alternatively, references may be used to refer to the various derived employee objects.

Code Block
bgColor#ccccff
langcpp
int main () {
  Employee coder("Joe Smith");
  Employee typist("Bill Jones");
  Manager designer("Jane Doe", typist);

  Employee &toPrint = designer;  // Jane remains entire
  toPrint.print();
}

Compliant Solution (Abstract Base Class)

The most effective way to avoid slicing of objects is to ensure, whenever possible, that polymorphic base classes are abstract.

Code Block
bgColor#ccccff
langcpp
class Employee {
public:
  Employee(string theName) : name(theName) {};
  virtual ~Employee();
  string getName() const {return name;}
  virtual void print() const = 0;
private:
  string name;
};

The presence of a pure virtual function in the Employee base class ensures that no objects of type Employee will exist, and slicing cannot occur.

Risk Assessment

Slicing results in information being lost, which could lead to a program not working properly and hence to a denial-of-service attack.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OOP33-CPP

low

probable

high

P2

L3

Bibliography

[Dewhurst 02] Gotcha #38, "Slicing"
[ISO/IEC 14882-2003] Section 9, "Classes"
[Sutter 00] GotW #22: "Object Lifetimes - Part I"


OOP31-CPP. Ensure object construction invocations isn't mistaken for a function variable declaration      13. Object Oriented Programming (OOP)      OOP34-CPP. Ensure the proper destructor is called for polymorphic objects