2017-02-05 6 views
0

Мой проект для домашних животных подошел к точке, где я должен начать отслеживать продолжительность жизни указателя, и я пытаюсь разработать для нее некоторую систему. К сожалению, популярный совет по использованию интеллектуальных указателей везде не применяется, поскольку Qt API сам использует голые указатели в каждом случае. Итак, что я придумал это:Как хранить указатели в Qt?

  1. Для всего принадлежащего Qt,

    • использование указателей обнаженные локально;
    • передают им между функциями голые тоже;
    • храните их в качестве подкласса QPointer, который составляет isNull() перед переходом на голый.
  2. Для всего, что принадлежит мне, используйте интеллектуальные указатели, как указано. Я собираюсь пойти с версиями std:: здесь.

  3. Дело, которое меня беспокоит. Для объектов, которые переключаются на владение (например, виджеты, добавленные/удаленные из макета)

    • использовать, хранить, пропустить указатели голые;
    • delete их вручную при необходимости.

Предложение, комментарии, советы? Я не очень люблю эту схему.

+0

просто используйте умные указатели 'std ::', в чем проблема с ними? небольшое обслуживание? он будет окупиться .. –

+0

@Abhinav Gauniyal Какой из 'std ::' умных указателей вы бы предложили для случая, когда я 1) создал виджет, 2) передал его в макет, и он станет принадлежащим Qt, 3) * возможно * удалить его из макета позже в зависимости от действий пользователя? Удаление чего-то принадлежащего Qt является сбоем. – sigil

+0

«Удаление чего-то принадлежащего Qt - это сбой». Ну нет. В общем, нет. Предположим, у вас есть виджет, который является дочерним элементом другого виджета: вы можете удалить этого ребенка напрямую, даже если он «принадлежит Qt» (через родительский виджет). Поэтому я хотел бы получить больше разъяснений о том, что вас беспокоит, и как именно вы не можете найти решение для этого. – peppe

ответ

2

Прежде всего, держите вещи по достоинству, где сможете. Просмотрите каждое использование new, make_unique и make_shared с подозрением - вы должны оправдать создание каждого динамического объекта. Если подпоследовательность имеет такое же время жизни, что и родительское, то удерживание по значению не требует больших усилий. Например:

class MyWidget : public QWidget { 
    Q_OBJECT 
    QGridLayout m_topLayout{this}; 
    QLabel m_sign{"Hello World"}; 
public: 
    MyWidget(QWidget * parent = nullptr) : QWidget{parent} { 
    m_topLayout.addWidget(&m_sign, 0, 0); 
    } 
}; 

Вы проезжаете указатели вокруг, но владение объектами ясно, и смена владельца не изменилась. Просто потому, что у QObject есть родительский элемент, это не означает, что родитель «владеет» им. Если ребенок разрушен до родителя, право собственности прекращается. Используя семантику C++, а именно четко определенный порядок построения и уничтожения членов, вы имеете полный контроль над дочерними жизнями, а родитель QObject не вмешивается.

Если у вас есть недвижные объекты, у которых есть один владелец, используйте std::unique_ptr и переместите его.Это способ динамически создать QObject вокруг вашего собственного кода. Вы можете удалить их из указателя в том месте, где вы делаете свое владение, управляемым родителем QObject, если таковое имеется.

Если у вас есть объекты с общим достоянием, где их жизнь должна заканчиваться как можно скорее (в случае прекращения действия приложения или уничтожения какого-либо долгоживущего объекта), используйте std::shared_ptr. Убедитесь, что указатель выделяет пользователей. Например:

class MyData : public QAbstractItemModel { /* ... */ }; 

class UserWindow : public QWidget { 
    Q_OBJECT 
    std::shared_ptr<MyData> m_data; // guaranteed to outlive the view 
    QTreeView m_view; 
public: 
    void setData(std::shared_ptr<MyData> && data) { 
    m_data = std::move(data); 
    m_view.setModel(m_data.data()); 
    } 
}; 

Этот пример, возможно, надуманным, так как в Qt большинство пользователей объектов смотреть destroyed() сигнал объекта и реагировать на уничтожение объектов. Но это имеет смысл, если, например, m_view был сторонним дескриптором API C API, который не мог отслеживать время жизни объекта данных.

Если владение объектом является общим для потоков, то использование std::shared_ptr имеет важное значение: сигнал destroyed() доступен только для одного потока. Когда вы узнаете об удалении объекта в другом потоке, уже слишком поздно: объект уже уничтожен.

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

+0

Я добавил ваш замечательный ответ для дальнейшего использования, когда мой проект продвигается. Кроме того, я думаю, что я начну отслеживать ваши сообщения. – sigil

1

Для начинающих, в Риме, быть римскими.
QT был разработан в начале 90-х годов, и это было большим успехом одновременно.
сожалению, QT действительно не были приняты новые функции, как время шло так сам API имеет очень старом стиле C++ к нему (и я могу сказать, стиль Java к нему?)

вы не можете заставить QT внезапно стать C++ 14, потому что это не так. используйте соглашения популярного QT, когда дело доходит до QT. используйте исходный указатель, если это была цель разработки платформы. используйте типы значений, если вы не можете.

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

+6

Насколько я помню, Римская империя продолжала течь в памяти, но, в конце концов, была прервана. –

+0

@ Давид Хаим Не могли бы вы указать мне список идиом Qt, которые вы упомянули? Я попытался найти именно это, прежде чем задавать свой вопрос, но все, что может предложить Google, - это одна презентация .pdf - фрагментарная, отрывочная и не очень полезная. – sigil

+0

@KerrekSB - в исходных указателях нет ничего плохого, так вы их используете. – dtech