2015-10-20 1 views
2
class A : public QObject { 
    A() : b(this) {} // ! 


    class B : public QObject { 
    B(QObject* parent) 
    }; 
    B b; 
} 

Должен ли я установить родительский объект, если дочерний объект не является динамическим? какая разница между обоими случаями?Что лучше? Установить родительский родитель QObject для ребенка или нет, если ребенок является членом класса?

Что лучше и какая разница?

class A : public QObject { 
    A() : b(new B(this)) {} // ! 


    class B : public QObject { 
    B(QObject* parent) 
    }; 
    B* b; 
} 

или

class A : public QObject { 
    A() : b(new B()) {} // ! 


    class B : public QObject { 
    B() 
    }; 
    smart_ptr<B> b; 
} 

ответ

1

Механизм родитель/ребенок немного больше, чем просто удаление объекта, когда его родителей разрушается. Например, дочерние объекты всегда будут выполняться в том же потоке, что и их родительский элемент, и если поток родителей изменен, дочерние элементы тоже будут перемещаться. Но только один из многих примеров. У вас не будет этих функций, если вы используете smart_ptr<B> b. Для этого эквивалентом в Qt будет QScopedPointer. Поэтому лучше использовать родительский/дочерний механизм QObject, если это возможно. Дополнительная информация обо всем QObject-parent-child-mechan, см. Object Trees & Ownership

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

Но самое главное: кажется, вы забыли передать родительский объект в конструктор QObject! (Исправьте меня, если вы просто оставили его, чтобы упростить кодовые блоки). Если вы не сделаете этого, ваш объект не станет ребенком его родители:

class A : public QObject { 
    A(QObject *parent = NULL) : 
    QObject(parent),//Here, because A may become a child of another object some time 
    b(new B(this)) 
    {} 


    class B : public QObject { 
    B(QObject *parent = NULL) : 
     QObject(parent)//And here it's neccessary 
    {} 
    }; 
    B* b; 
} 
2

QObject происхождения и продолжительность хранения являются ортогональными вопросами и должно быть рассмотрено отдельно.

Вы не можете переместить QObject, удерживая другое QObject s в другой поток, если все принадлежащие ему объекты не имеют владельца, установленного в качестве родителя. Таким образом, не устанавливая родителя, вы преждевременно ограничиваете функциональность родительского класса.

В современном C++ дочерний элемент QObject с динамическим временем хранения должен храниться через диспетчер ресурсов. Такой менеджер может быть умным указателем или просто владеющим объектом. Напомним, что QObject также неявно является контейнером QObject. Из-за этого даже необязательно иметь явный указатель элемента для дочернего объекта - скажем, если вам нужно только обратиться к объекту непосредственно во время строительства владельца.

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

Так что, если вы не используете PIMPL idiom, то все члены должны иметь автоматическую продолжительность хранения в самом объекте:

// A.h 
class A : public QObject { 
    Q_OBJECT 
    QSerialPort m_port { this }; 
    QThread m_thread { this }; 
    .... 
}; 

В противном случае, они принадлежат в Pimpl:

// A.h 
class APrivate; 
class A : public QObject { ... }; 

// A.cpp 
#include "A.h" 
class APrivate { 
public: 
    A * const q_ptr; 
    QSerialPort m_port { q_ptr }; 
    QThread m_thread { q_ptr }; 
    APrivate(A * q) : q_ptr(q) {} 
}; 

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

// DO NOT WRITE CODE LIKE THIS!! 
class QSerialPort; 
class QThread; 
class A : public QObject { 
    Q_OBJECT 
    QSerialPort * m_port; 
    QThread * m_thread; 
    ... 
}; 
// DO NOT WRITE CODE LIKE THIS!! 
Смежные вопросы