Conversion or deletion of incomplete class pointer

Вы удаляете или приводите к указателю на неполный класс

Описание

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

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

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

Риск

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

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

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

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

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

Также можно выполнить одно из следующих действий:

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

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

Примеры

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

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

Информация о результатах

Группа: Объектно-ориентированная
Язык: C++
По умолчанию: On для рукописного кода, off для сгенерированного кода
Синтаксис командной строки : INCOMPLETE_CLASS_PTR
Влияние: Средний
Введенный в R2018b