AUTOSAR C++ 14 правил A5-3-3

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

Описание

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

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

Объяснение

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

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

Реализация 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 когда указатель 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;
}

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

Группа: Выражения

Введенный в R2019a