|
|||||||
Появление предупреждения о неизвестном (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, будет предаваться "по цепочке" из класса в класс или в составе другого объекта, включающего в себя "проблеммный" класс. В этом случае надо смотреть где происходит использование объекта данного класса, особенно если где-то создается экземпляр данного класса (а следовательно и уничтожается). В этом месте надо надо так же выполнить все условия, описанные выше. Вообще, по-хорошему, надо в проектах использовать подход "всегда пиши виртуальный деструктор". При таком подходе проблем будет меньше, а описанное в этой статье предупреждение вообще не появится. Но если руководитель проекта не совсем в адеквате, и требует чтобы лишних деструкторов небыло, то стоит знать о вот такой особенности, возникающей при компиляции. |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|