MyTetra Share
Делитесь знаниями!
Появление предупреждения о неизвестном (incomplete) типе, используемом в QSharedPointer
Время создания: 06.08.2024 17:55
Текстовые метки: qt, qt4, qt5, c++, си++, предупреждение, warning, тип, incomplete, delete, QSharedPointer, умный, указатель, удаление, деструктор, destructor, QObject, include, класс, реализация
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Принципы написания кода
Запись: xintrea/mytetra_syncro/master/base/17229561457ufpokfksj/text.html на raw.github.com

Ситуация следующая. Имеется класс SynchroManager, унаследованный от QObject. В его свойствах используется умный указатель QSharedPointer, который хранит указатель на описанный в другом месте класс DeliveryClient. Чтобы шла компиляция, этот DeliveryClient предварительно описывается в заголовочном файле SynchroManager.



Примечание: на месте QSharePointer может быть любой другой тип умного указателя.



Вот как выглядит заголовочный файл (*.h) класса SynchroManager:



#include <QObject>


// Предварительное описание DeliveryClient

class DeliveryClient;


class SynchroManager : public QObject

{

Q_OBJECT


public:

explicit SynchroManager(const QString &name,

const SynchroSettings &settings,

QObject *parent = nullptr);


private:

QSharedPointer<DeliveryClient> m_deliveryClient;

...

}



В файле реализации (*.cpp) прописан инклюд на DeliveryClient, прописана реализация конструктора SynchroManager и прочих методов:



#include "delivery_client.h"

#include "syncro_manager.h"


SynchroManager::SynchroManager(const QString &name,

const SynchroSettings &settings,

QObject *parent) :

QObject(parent)

{

...

}



И при компиляции такого класса появляется странное предупреждение:



In file included from /usr/include/qt4/QtCore/qsharedpointer.h:50:0,

from /usr/include/qt4/QtCore/QSharedPointer:1,

from build/moc/../../src/database_synchro/synchro_manager.h:5,

from build/moc/moc_synchro_manager.cpp:9:

/usr/include/qt4/QtCore/qsharedpointer_impl.h: In instantiation of

‘static void QtSharedPointer::ExternalRefCount<T>::

deref(QtSharedPointer::ExternalRefCount<T>::Data*, T*)

[with T = DeliveryClient;

QtSharedPointer::ExternalRefCount<T>::Data =

QtSharedPointer::ExternalRefCountData]’:

/usr/include/qt4/QtCore/qsharedpointer_impl.h:336:16:

required from ‘void QtSharedPointer::ExternalRefCount<T>::deref()

[with T = DeliveryClient]’

/usr/include/qt4/QtCore/qsharedpointer_impl.h:401:43:

required from ‘QtSharedPointer::ExternalRefCount<T>::~ExternalRefCount()

[with T = DeliveryClient]’

/usr/include/qt4/QtCore/qsharedpointer_impl.h:466:7:

required from here

/usr/include/qt4/QtCore/qsharedpointer_impl.h:342:21:

warning: possible problem detected in invocation of delete operator:

[-Wdelete-incomplete]

delete value;

^~~~~~



То есть, сборка пройдет, но будет болтаться предупреждение об неизвестном типе, причем непонятно где неизвестном: в заголовке-то тип предварительно объявлен, и, по классике, используется в виде указателя (умного). А в реализации тип известен, так как подключен заголовок с его описанием. В чем может быть проблема?



А проблема в том, что в написанном таким образом классе, в контексте деструктора по-умолчанию, предварительно объявленный тип DeliveryClient неизвестен. И, соответственно, таковой указатель не может быть нормально удален.


Другими словами: в приведенном коде просто нет описания деструктора, и поэтому будет компилироваться деструктор по-умолчанию. А у такого деструктора в его контексте нет сведений о классе DeliveryClient.



Как решить эту проблему? Достаточно просто добавить в заголовочный файл (*.h) прототип деструктора:



~SynchroManager();



А в файл реализации (обязательно в файл реализации, а не в заголовочный) добавить реализацию пустого деструктора:



SynchroManager::~SynchroManager()

{


}



И все волшебным образом будет компилироваться без предупреждений. Реализацию конструктора необходимо помещать именно в *.cpp - файл потому, что в нем есть инклуд для delivery_client.h, и его наличие приводит к тому, что при компиляции даже пустого деструктора, в контексте этого деструктора уже известно описание класса DeliveryClient.



Важно: надо обязательно проверить, действительно ли в файле реализации имеется инклюд на заголовок, в котором описан тип, используемый через QSharedPointer. Если будет правильно написан прототип и реализация деструктора, но не будет этого инклюда, то если класс еще не дописан и в нем нет использования объекта через QSharedPointer, то объектник будет компилироваться, но вышеуказанное предупреждение сохранится.


И еще: данное предупреждение может возникать и в случае, если объект, передаваемый через QSharePointer, будет предаваться "по цепочке" из класса в класс или в составе другого объекта, включающего в себя "проблеммный" класс. В этом случае надо смотреть где происходит использование объекта данного класса, особенно если где-то создается экземпляр данного класса (а следовательно и уничтожается). В этом месте надо надо так же выполнить все условия, описанные выше.



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


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования