CERT C++: EXP57-CPP

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

Описание

Управляйте определением

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

Примеры

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

Описание

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

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

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

Риск

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

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

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

Фиксация

Когда вы удаляете или удрученный к указателю на класс, убедитесь, что определение класса отображается.

В качестве альтернативы можно выполнить одно из этих действий:

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

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

Пример - удаление указателя на неполный класс

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() {}
    // ...
};

Пример - Downcasting к указателю на неполный класс

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. Эта информация о множественном наследовании не доступна при downcasting в File1.cpp. Результат downcasting передается функциональному funcprint и разыменованный в теле funcprint. Поскольку downcasting был сделан с неполной информацией, разыменовывание может быть недопустимым.

Коррекция — задает класс перед Downcasting

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

В этом откорректированном примере downcasting сделан в File2.cpp в теле funcprint в точке, где определение класса Derived отображается. downcasting не сделан в 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 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

Это программное обеспечение и сопоставленная документация не были рассмотрены, ни являются подтвержденным Университетом Карнеги-Меллон или его Институтом программной инженерии.