Main Content

CERT C++: EXP57-CPP

Do not cast or delete pointers to incomplete classes

Description

Rule Definition

Do not cast or delete pointers to incomplete classes.1

Polyspace Implementation

The rule checker checks for Conversion or deletion of incomplete class pointer.

Examples

expand all

Issue

Conversion or deletion of incomplete class pointer occurs when you delete or cast to a pointer to an incomplete class. An incomplete class is one whose definition is not visible at the point where the class is used.

For instance, the definition of class Body is not visible when the delete operator is called on a pointer to Body:

class Handle {
  class Body *impl;  
public:
  ~Handle() { delete impl; }
  // ...
};

Risk

When you delete a pointer to an incomplete class, it is not possible to call any nontrivial destructor that the class might have. If the destructor performs cleanup activities such as memory deallocation, these activities do not happen.

A similar problem happens, for instance, when you downcast to a pointer to an incomplete class (downcasting is casting from a pointer to a base class to a pointer to a derived class). At the point of downcasting, the relationship between the base and derived class is not known. In particular, if the derived class inherits from multiple classes, at the point of downcasting, this information is not available. The downcasting cannot make the necessary adjustments for multiple inheritance and the resulting pointer cannot be dereferenced.

A similar statement can be made for upcasting (casting from a pointer to derived class to a pointer to a base class).

Fix

When you delete or downcast to a pointer to a class, make sure that the class definition is visible.

Alternatively, you can perform one of these actions:

  • Instead of a regular pointer, use the std::shared_ptr type to point to the incomplete class.

  • When downcasting, make sure that the result is valid. Write error-handling code for invalid results.

Example - Deletion of Pointer to Incomplete Class
class Handle {
  class Body *impl;  
public:
  ~Handle() { delete impl; } //Noncompliant
  // ...
};

In this example, the definition of class Body is not visible when the pointer to Body is deleted.

Correction — Define Class Before Deletion

One possible correction is to make sure that the class definition is visible when a pointer to the class is deleted.

class Handle {
  class Body *impl;  
public:
  ~Handle();
  // ...
};
 
// Elsewhere
class Body { /* ... */ };
  
Handle::~Handle() {
  delete impl;
}
Correction — Use std::shared_ptr

Another possible correction is to use the std::shared_ptr type instead of a regular pointer.

#include <memory>
  
class Handle {
  std::shared_ptr<class Body> impl;
  public:
    Handle();
    ~Handle() {}
    // ...
};
Example - Downcasting to Pointer to Incomplete Class

File1.h:

class Base {
protected:
  double var;
public:
  Base() : var(1.0) {}
  virtual void do_something();
  virtual ~Base();
};

File2.h:

void funcprint(class Derived *);
class Base *get_derived(); 

File1.cpp:

#include "File1.h"
#include "File2.h"
 
void getandprint() {
  Base *v = get_derived();
  funcprint(reinterpret_cast<class Derived *>(v)); //Noncompliant
}

File2.cpp:

#include "File2.h"
#include "File1.h"
#include <iostream>
 
class Base2 {
protected:
  short var2;
public:
  Base2() : var2(12) {}
};
 
class Derived : public Base2, public Base {
  float var_derived;
public:
    Derived() : Base2(), Base(), var_derived(1.2f) {}
    void do_something()
    {
        std::cout << "var_derived: "
                  << var_derived << ", var : " << var
                  << ", var2: " << var2 << std::endl;
    }
 };
 
void funcprint(Derived *d) {
  d->do_something();
}
 
Base *get_derived() {
  return new Derived;
}

In this example, the definition of class Derived is not visible in File1.cpp when a Base* pointer to downcast to a Derived* pointer.

In File2.cpp, class Derived derives from two classes, Base and Base2. This information about multiple inheritance is not available at the point of downcasting in File1.cpp. The result of downcasting is passed to the function funcprint and dereferenced in the body of funcprint. Because the downcasting was done with incomplete information, the dereference can be invalid.

Correction — Define Class Before Downcasting

One possible correction is to define the class Derived before downcasting a Base* pointer to a Derived* pointer.

In this corrected example, the downcasting is done in File2.cpp in the body of funcprint at a point where the definition of class Derived is visible. The downcasting is not done in File1.cpp where the definition of Derived is not visible. The changes from the previous incorrect example are highlighted.

File1_corr.h:

class Base {
protected:
  double var;
public:
  Base() : var(1.0) {}
  virtual void do_something();
  virtual ~Base();
};

File2_corr.h:

void funcprint(class Base *);
class Base *get_derived(); 

File1.cpp:

#include "File1_corr.h"
#include "File2_corr.h"
 
void getandprint() {
  Base *v = get_derived();
  funcprint(v);
}

File2.cpp:

#include "File2_corr.h"
#include "File1_corr.h"
#include <iostream>
 
class Base2 {
protected:
  short var2;
public:
  Base2() : var2(12) {}
};
 
class Derived : public Base2, public Base {
  float var_derived;

public:
    Derived() : Base2(), Base(), var_derived(1.2f) {}
    void do_something()
    {
        std::cout << "var_derived: "
                  << var_derived << ", var : " << var
                  << ", var2: " << var2 << std::endl;
    }
};
 
void funcprint(Base *d) {
  Derived *temp = dynamic_cast<Derived*>(d);
  if(temp)  {
     d->do_something();
  }
  else {
      //Handle error
  }
}
 
Base *get_derived() {
  return new Derived;
}

Check Information

Group: 02. Expressions (EXP)

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.