AUTOSAR C++14 Rule A5-3-3

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

Описание

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

Указатели на неполные типы классов не должны удаляться.

Объяснение

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

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

Реализация Polyspace

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

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

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

Поиск и устранение проблем

Если вы ожидаете нарушения правил, но не видите его, обратитесь к разделу «Стандартные нарушения кодирования не отображаются».

Примеры

расширить все

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 когда a Base* указатель на нисходящий вызов на Derived* указатель.

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

Коррекция - задайте класс перед понижением значения

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

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

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

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

Группа: Выражения
Категория: Необходимый, Автоматизированный
Введенный в R2019a