exponenta event banner

CERT C++: EXP57-CPP

Не приводить и не удалять указатели на неполные классы

Описание

Определение правила

Не выводить и не удалять указатели на неполные классы. [1 ]

Внедрение Polyspace

Эта проверка проверяет преобразование или удаление неполного указателя класса.

Примеры

развернуть все

Проблема

Преобразование или удаление неполного указателя класса происходит при удалении или приведении указателя к неполному классу. Неполный класс - это класс, определение которого не отображается в точке использования класса.

Например, определение класса Body не виден, когда delete вызывается оператор по указателю на Body:

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

Риск

При удалении указателя на неполный класс невозможно вызвать какой-либо нетривиальный деструктор, который может быть у класса. Если деструктор выполняет операции очистки, такие как освобождение памяти, эти операции не выполняются.

Подобная проблема возникает, например, при понижении канала до указателя на неполный класс (понижающий канал - это переход от указателя на базовый класс к указателю на производный класс). В точке нисходящего вещания связь между базовым и производным классом неизвестна. В частности, если производный класс наследует от нескольких классов, в момент понижения, эта информация недоступна. Нисходящая трансляция не может произвести необходимые корректировки для множественного наследования, и результирующий указатель не может быть удален.

Подобная инструкция может быть сделана для передачи (приведение от указателя к производному классу к указателю к базовому классу).

Зафиксировать

При удалении или переходе к указателю на класс убедитесь, что определение класса видно.

Кроме того, можно выполнить одно из следующих действий:

  • Вместо обычного указателя используйте std::shared_ptr введите для указания на неполный класс.

  • При спуске убедитесь, что результат действителен. Запись кода обработки ошибок для недопустимых результатов.

Пример - Удаление указателя на неполный класс
class Handle {
  class Body *impl;  
public:
  ~Handle() { delete impl; } 
  // ...
};

В этом примере определение класса Body не виден, когда указатель на Body удаляется.

Исправление - Определение класса перед удалением

Одной из возможных корректировок является проверка видимости определения класса при удалении указателя на класс.

class Handle {
  class Body *impl;  
public:
  ~Handle();
  // ...
};
 
// Elsewhere
class Body { /* ... */ };
  
Handle::~Handle() {
  delete impl;
}
Коррекция - использование std::shared_ptr

Другой возможной поправкой является использование std::shared_ptr введите вместо обычного указателя.

#include <memory>
  
class Handle {
  std::shared_ptr<class Body> impl;
  public:
    Handle();
    ~Handle() {}
    // ...
};
Пример - Понижение частоты до указателя на неполный класс

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));
}

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;
}

В этом примере определение класса Derived не виден в File1.cpp когда Base* указатель на понижение до Derived* указатель.

В File2.cpp, класс Derived происходит от двух классов, Base и Base2. Эта информация о множественном наследовании недоступна на этапе понижения в File1.cpp. Результат понижения передается функции funcprint и в тексте funcprint. Поскольку нисходящая трансляция была выполнена с неполной информацией, отмена передачи может быть недопустимой.

Коррекция - Определение класса перед понижением

Одной из возможных корректировок является определение класса. Derived перед понижением Base* указатель на Derived* указатель.

В этом скорректированном примере нисходящая трансляция выполняется в File2.cpp в теле funcprint в точке, где определение класса Derived виден. Понижение не выполняется в File1.cpp где определение Derived не виден. Изменения предыдущего неверного примера подсвечиваются.

File1.h:

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

File2.h:

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

File1.cpp:

#include "File1.h"
#include "File2.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;
}

Проверить информацию

Группа: 02. Выражения (EXP)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.