exponenta event banner

Правило AUTOSAR C++ 14 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 когда Base* указатель на понижение до Derived* указатель.

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

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

Одной из возможных корректировок является определение класса. 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