ПроблемаПреобразование или удаление неполного указателя класса происходит при удалении или приведении к указателю на неполный класс. Неполный класс является классом, определение которого не отображается в точке, где используется класс.
Например, определение 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.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;
}