ПроблемаПреобразование или удаление неполного указателя класса происходит при удалении или приведении указателя к неполному классу. Неполный класс - это класс, определение которого не отображается в точке использования класса.
Например, определение класса Body не виден, когда delete вызывается оператор по указателю на Body:
class Handle {
class Body *impl;
public:
~Handle() { delete impl; }
// ...
}; РискПри удалении указателя на неполный класс невозможно вызвать какой-либо нетривиальный деструктор, который может быть у класса. Если деструктор выполняет операции очистки, такие как освобождение памяти, эти операции не выполняются.
Подобная проблема возникает, например, при понижении канала до указателя на неполный класс (понижающий канал - это переход от указателя на базовый класс к указателю на производный класс). В точке нисходящего вещания связь между базовым и производным классом неизвестна. В частности, если производный класс наследует от нескольких классов, в момент понижения, эта информация недоступна. Нисходящая трансляция не может произвести необходимые корректировки для множественного наследования, и результирующий указатель не может быть удален.
Подобная инструкция может быть сделана для передачи (приведение от указателя к производному классу к указателю к базовому классу).
ЗафиксироватьПри удалении или переходе к указателю на класс убедитесь, что определение класса видно.
Кроме того, можно выполнить одно из следующих действий:
Вместо обычного указателя используйте std::shared_ptr введите для указания на неполный класс.
При спуске убедитесь, что результат действителен. Запись кода обработки ошибок для недопустимых результатов.
Пример - Удаление указателя на неполный класс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.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;
}