2016-06-14 3 views
0

Я ищу лучшие практики с интеллектуальными указателями Qt. Скажем, у меня есть что-то вроде этого.Qt умные указатели в функциональных параметрах

void aFunction() { 
    QSharedPointer<QVector<QSharedPointer<Object> > > pointer = QSharedPointer<QVector<QSharedPointer<Object> > >(new QVector<QSharedPointer<Object> >); 

    doSomethingWithPointer(pointer); 
    changeOwnership(pointer); 
} 

Тогда функция doSomethingWithPointer

void doSomethingWithPointer(QSharedPointer<QVector<QSharedPointer<Object> > > pointer) { 
    pointer->append(QSharedPointer<Object>(new Object); 
} 

Теперь я после doSomethingWithPointer, я передаю указатель на changeOwnership, который должен взять на себя ответственность его. Что мне интересно, так это doSomethingWithPointer. Можно ли передавать общие указатели в такой функции? Или я должен использовать QWeakPointer, поскольку doSomethingWithPointer фактически не имеет права владения указателем? Когда я должен использовать QWeakPointer?

+2

Лучшая практика QTSmartPointers, чтобы никогда не использовать QTSmartPointers :) – SergeyA

+1

[Это] (http://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful) и [это] (http://stackoverflow.com/questions/8385457/should-i-pass-a-shared-ptr-by-reference) в значительной степени суммирует его. Они о стандартной библиотеке, но в чем разница? – LogicStuff

ответ

3

Не имеет смысла передавать слабый указатель, так как единственный способ безопасного использования слабого указателя - вернуть его в общий указатель. В этом отношении api несколько искажен, так как позволяет использовать QWeakPointer::data() для обмана - но это глупо, вы никогда не должны использовать его таким образом. std::weak_ptr получил это право, и вы не можете использовать его, кроме как лить его на std::shared_ptr.

Но все равно это не обязательно.

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

Ответ в вашем конкретном случае: вообще не используйте внешний общий указатель.

struct Object {}; 

void appendObject(QVector<QSharedPointer<Object>> & data) { 
    data.append(QSharedPointer<Object>(new Object)); 
} 

void use(QVector<QSharedPointer<Object>> && data) { 
} 

int main() { 
    QVector<QSharedPointer<Object>> data; 
    appendObject(data); 
    use(std::move(data)); 
} 

Врезка: В C++ 11 вы бы ожидать, что uniform initialization работать - но не с момента QVector и/или QSharedPointer имеют несовместимые API. Так что это было бы хорошо, но не кости:

data.append({new Object}); 

Было бы приемлемо, чтобы написать это в Qt 4 стиля и зависят от неявного совместного использования контейнеров Qt, но этот стиль является преждевременным pessimization в Qt 5/C++ 11:

void use(QVector<QSharedPointer<Object>> & data) { 
} 

int main() { 
    QVector<QSharedPointer<Object>> data; 
    appendObject(data); 
    use(data); 
} 

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

Даже если это не так, если вы на самом деле не нужно долевой собственности Object, а лишь динамически распределяемой контейнер, вы можете использовать QList вместо:

struct Object {}; 

void appendObject(QList<Object> & data) { 
    data << Object(); 
} 

void use(QList<Object> && data) { 
} 

int main() { 
    QList<Object> data; 
    appendObject(data); 
    use(std::move(data)); 
} 

Если объект не копируемыми, вы должны использовать std::list и использовать перемещение семантику для смены владельца:

void appendObject(std::list<QObject> & data) { 
    data.emplace_back(); 
} 

void use(std::list<QObject> && data) { 
} 

int main() { 
    std::list<QObject> data; 
    appendObject(data); 
    use(std::move(data)); 
} 

Наконец, если класс вы управляете происходит от QObject, вы могли бы использовать QObject будучи QObject контейнер:

void appendObject(QObject * container) { 
    new QObject(container); // add a child 
} 

void use(QScopedPointer<QObject> && container) { 
    for (auto object : container->children()) 
    qDebug() << object; 
} 

int main() { 
    QScopedPointer<QObject> container; 
    appendObject(container.data()); 
    use(std::move(container)); 
} 

Поскольку QObject внутренне использует Pimpl, we can too, и таким образом избавиться от глупой внешнего указателя.В конце концов, QObject - это QScopedPointer к его pimpl, как и большинство других классов Qt!

#include <QtCore> 
#include <private/qobject_p.h> 

class Container : public QObject { 
public: 
    Container(QObject * parent = 0) : QObject(parent) {} 
    Container(Container && other) : 
     QObject(*new QObjectPrivate, other.parent()) { 
     d_ptr.swap(other.d_ptr); 
    } 
}; 

void appendObject(Container & container) { 
    new QObject(&container); // add a child 
} 

void use(Container && container) { 
    for (auto object : container.children()) 
    qDebug() << object; 
} 

int main() { 
    Container container; 
    appendObject(container); 
    appendObject(container); 
    use(std::move(container)); 
} 
+0

Ничего себе, спасибо за подробный ответ. Это проясняет ситуацию. – joefuldoe

Смежные вопросы