LINUX.ORG.RU

QObject и умные указатели

 ,


0

2

Собственно вопрос вкусовщины: кто как хранит QObject-based объекты?

Варианты:

Сырые указатели:

// .h
class MyObject : public QObject
{
    Q_OBJECT

public:
    explicit MyObject(QObject *parent = nullptr);

private:
    MyOtherObject * const m_d;
}

// .cpp
MyObject::MyObject(QObject *parent)
  : QObject(parent),
    m_d(new MyOtherObject(this))
{
    connect(m_d, ...);
}

Умные указатели:

// .h
class MyObject : public QObject
{
    Q_OBJECT

public:
    explicit MyObject(QObject *parent = nullptr);

private:
    // или QScopedPointer
    const std::unique_ptr<MyOtherObject> m_d;
}

// .cpp
MyObject::MyObject(QObject *parent)
  : QObject(parent),
    m_d(std::make_unique<MyOtherObject>())
{
    // лишний get()...
    connect(m_d.get(), ...);
}

Собственно разница минимальная. Разве что в первом случае можно забыть this. А во втором нужно писать лишний get().

QObject заточен под сырые указатели и с этим ничего не поделать.

Возможно у кого-то есть самописный connect11(), который умеет работать с unique/shared_ptr.

Сырые указатели, не помню когда последний раз были проблемы с утечкой памяти. Больше всего в умных меня раздражает многословность, да и встроенный механизм с родителем перекрывает большую часть сценариев.

CrossFire ★★★★★ ()
Последнее исправление: CrossFire (всего исправлений: 1)

Первое, только не сырой указатель, а QPointer.

anonymous ()

Собственно разница минимальная.

А в первом случае при исключении внутри конструктора разве MyOtherObject объект освободится?

xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 2)
Ответ на: комментарий от xaizek

А в первом случая

В первом случае сработает деструктор QObject, который удалит дочерний объект.

anonymous ()

В этом случае нужно хранить не указатель, а значение.

Если всё-таки нужен полиморфизм, я бы выбрал unique_ptr, и коннектил либо через m_d->connect, либо через &*m_d. Но это вкусовщина, так как если сделать this родителем m_d, то эффект будет тот же.

vzzo ★★★ ()
Ответ на: комментарий от vzzo

В этом случае нужно хранить не указатель, а значение.

QObject нельзя хранить как значение, насколько я помню.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Можно. Его нельзя копировать/перемещать, но т.к. родитель — это QObject, то это и не нужно.

vzzo ★★★ ()
Ответ на: комментарий от RazrFalcon

QObject нельзя хранить как значение, насколько я помню

Откуда инфа?

I-Love-Microsoft ★★★★★ ()

Есть же кутешные QPointer, QSharedPointer, QSharedDataPointer и прочие.

wolph ★★ ()
Ответ на: комментарий от RazrFalcon

QObject нельзя хранить как значение, насколько я помню.

А какая нафиг разница - хранить как значение или в std::unique_ptr? Объекту все-равно где в памяти он находится, а время жизни по факту одно и то же.

anonymous ()
Ответ на: комментарий от wolph

Есть же кутешные QPointer, QSharedPointer, QSharedDataPointer и прочи

QPointer - да, хорошая замена сырому указателю, т.к. QPointer автоматически обнуляется при удалении объекта; QSharedPointer - альтернатива std::shared_ptr; QSharedDataPointer - не для этого случая.

anonymous ()
Ответ на: комментарий от anonymous

В QPointer главная фишка это в том, что ты копируешь кучу этих купойнтеров - и если один из них удалил/обнулил - остальные тоже нулями станут, они не будут указывать на невальдные устаревшие указатели.

Ну а с удалением и временем жизни нет никакого смысла заморачиваться, там же родитель своих предков чистит, причем ЕМНИП возможно отложенно. Кутешные вещи же ЕМНИП удаляются не через delete ptr, а как ptr->deleteLater()

wolph ★★ ()

[Offtopic]
Накипело:

Я на работе всё время вожусь с STL причем с C++14/C++17. Недавно залез в свой старый проект на Qt и помимо плевков от стыда за свой старый код я еще испытал средне-высокой температуры подгорание от степени «деревянности» классов Qt в сравнении, собственно, со стандартной библиотекой.
[/Offtopic]

А касательно вопроса: система иерархии QObject нормально работает с удалением ненужных обьектов, главное завести себе правило назначать каждому QObject родителя (вместо привычки впихивать unique_ptr везде).

KennyMinigun ★★★★★ ()

Классика из жанра «мучительные думки цепепе программиста» :-) Лол :-) Ну а в Куте наглядно демонстрирует «ехал пимпл через пимпл, пимпл пимплом погонял» :-) Фактически, каждый new в Qt приводит к 2-м аллокациям, что не может не доставлять :-) Лол :-) И не важно, какие там оптимизации pimpl (fast или ultra-fast), и не важно какие там перегрузки new, важно что аллокаций на каждый чих 2 :-) Хвалёная эффективность цепепе как она есть :-) Лол :-)

anonymous ()
Ответ на: комментарий от KennyMinigun

О, привет, смайломен. Зарегистрируйся, а?

Привет :-) Это ещё зачем? :-)

anonymous ()
Ответ на: комментарий от anonymous

Хвалёная эффективность цепепе как она есть :-) Лол :-)

Qt писался с расчётом на мега-суровые, бессмысленные и беспощадные, драматичные и просто невыносимые ограничения С++03. Отсюда и ноги растут.

// Кстати, оно еще компилируется под С++03?

KennyMinigun ★★★★★ ()
Ответ на: комментарий от KennyMinigun

Qt писался с расчётом на мега-суровые, бессмысленные и беспощадные, драматичные и просто невыносимые ограничения С++03. Отсюда и ноги растут.

Агась :-) А то сейчас в шедеврах Майерса про «эффективное использование» (лол) не расписывается pimpl и что теперь то, в современном цепепе можно и нужно pimpl тулить на смарт-поинтерах :-) Теперь это называется «драматичные и просто невыносимые ограничения цепепе-17», да? :-) Лол :-)

Ну а Qt :-) Это чуть ли не единственное из-за чего вообще стоит писать на цепепе :-)

anonymous ()
Ответ на: комментарий от KennyMinigun

Qt писался с расчётом на мега-суровые, бессмысленные и беспощадные, драматичные и просто невыносимые ограничения С++03.

Поправочка: ограничения C++ времен 1994-го года, когда даже C++98 был лишь в проекте и далеко не все C++ компиляторы умели в пространства имен, не говоря уже про исключения и шаблоны.

eao197 ★★★★★ ()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Qt4 писался так, чтобы переход с Qt3 был возможен и максимально прост. Чем, собственно, многие и пользовались. Так что последствия решений, принятых в 1993-94-х годах в Qt присутствуют до сих пор.

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Qt4 писался так, чтобы переход с Qt3 был возможен и максимально прост.

Вы опять путаете Qt4->Qt5 и Qt3->Qt4.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Ничего я не путаю. 100% совместимости не было, и переход с Qt3 на Qt4 требовал правки исходников. Но именно что правки, а не тотального переписывания.

eao197 ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Боюсь, все это сильно зависело от радиуса кривизны рук.

eao197 ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Что вы хотите сказать этими ссылками? В Qt4 принципиально модель владения поменялась? QObject исчез? QWidget удалили? moc-выбросили и система сигналов-слотов стала использовать другие макросы и работать стала принципиально иначе?

С использованием qt3support переход на Qt4 был достаточно простым. А потом уже можно было спокойно менять названия некоторых классов/методов/свойств в соответствии с тем, что сделали в Qt4.

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Проги всё равно пришлось с нуля переписывать. Достаточно взять тот же KDE4.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Проги всё равно пришлось с нуля переписывать.

Ну кому как. Там, где я работал, просто старый код постепенно портировали и все.

Достаточно взять тот же KDE4.

У KDE могла быть куча своих причин сделать полный реврайт, а Qt4 этому только поспособствовал.

Ну и я больше говорю про тот софт на Qt, который писался не just for fun, а для нужд бизнеса, на коммерческих лицензиях Qt,

eao197 ★★★★★ ()
Ответ на: комментарий от KennyMinigun

// Кстати, оно еще компилируется под С++03?

начиная с 5.7 на ubuntu 1204 не проходит configure

x905 ★★★★★ ()
Ответ на: комментарий от anonymous

ты позоришь доброе имя анонумоуса. зарегся, он тебя заигнорит.

да и вообще, ты порядком подз..л уже своим кукареканьем про очевидные вещи. куте делали не для скорости, а для портируемости. если ты там ищещ скорость то ты дол...б и это всё что тебе можно сказать в этом треде.

anonymous ()

Разве что в первом случае можно забыть this.

Это проблема дефольтного параметра конструктора:
explicit MyObject(QObject *parent = nullptr);

andreyu ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Сам QObject такой же.

И как это мешает избавиться от дефольтного параметра в наследнике?

andreyu ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Просто обычно именно так реализуют.

Наверное это специально для того, что бы потом сказать «Разве что в первом случае можно забыть this».

andreyu ★★★★★ ()

Я делаю так:

class MyObject : public QObject
{
    Q_OBJECT

public:
    explicit MyObject(QObject *parent = nullptr);

private:
    MyOtherObject  const m_d;
}

// .cpp
MyObject::MyObject(QObject *parent)
  : QObject(parent)
{
    connect(&m_d, ...);
}

LongLiveUbuntu ★★★★★ ()
Последнее исправление: LongLiveUbuntu (всего исправлений: 1)

Собственно разница минимальная

Обычно 1-й вариант, т.к., например не надо ничего модифицировать внутри класса если вдруг появится необходимость делать ему moveToThread.

kuzulis ()
Ответ на: комментарий от kuzulis

Я стараюсь не использовать объекты, перемещённые в поток, напрямую. Слишком легко выстрелить себе в ногу. Даже с сигналами/слотами.

RazrFalcon ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.