函数参数中的Qt智能指针

joe*_*doe 0 c++ qt

我正在寻找 Qt 智能指针的最佳实践。假设我有这样的事情。

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

    doSomethingWithPointer(pointer);
    changeOwnership(pointer);
}
Run Code Online (Sandbox Code Playgroud)

然后在 doSomethingWithPointer 函数中

void doSomethingWithPointer(QSharedPointer<QVector<QSharedPointer<Object> > > pointer) {
    pointer->append(QSharedPointer<Object>(new Object);
}
Run Code Online (Sandbox Code Playgroud)

现在我在 doSomethingWithPointer 之后,将指针传递给 changeOwnership,它应该拥有它的所有权。我想知道的是 doSomethingWithPointer。在这样的函数中传递共享指针可以吗?或者我应该使用 QWeakPointer 因为 doSomethingWithPointer 实际上并不拥有指针的所有权?我什么时候应该使用 QWeakPointer?

Rei*_*ica 6

传递弱指针没有多大意义,因为安全使用弱指针的唯一方法是将其强制转换回共享指针。Qt 的 api 在这方面有点坏,因为它允许你用来QWeakPointer::data()作弊——但这很愚蠢,你永远不会那样使用它。std::weak_ptr做对了,除了将它转换为std::shared_ptr.

但无论如何,这一切都不是必需的。

Qt 的容器是隐式共享值类。通过指针传递它们是多余的。A QVectorinternal 是指向其数据的共享指针。当您想要放弃其所有权时,您可以利用移动语义来减少否则会发生的隐式共享。

在您的特定情况下,答案是:根本不要使用外部共享指针。

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));
}
Run Code Online (Sandbox Code Playgroud)

边栏:在 C++11 中,您希望统一初始化可以工作 - 但它没有QVector和/或QSharedPointer具有不兼容的 API。所以这很好,但没有骰子:

data.append({new Object});
Run Code Online (Sandbox Code Playgroud)

以 Qt 4 风格编写它并依赖于 Qt 容器的隐式共享是可以接受的,但这种风格在 Qt 5/C++11 中是一种过早的悲观化:

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

int main() {
    QVector<QSharedPointer<Object>> data;
    appendObject(data);
    use(data);
}
Run Code Online (Sandbox Code Playgroud)

同样值得研究的是,是否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));
}
Run Code Online (Sandbox Code Playgroud)

如果对象不可复制,您应该使用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));
}
Run Code Online (Sandbox Code Playgroud)

最后,如果您管理的类是从 派生的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));
}
Run Code Online (Sandbox Code Playgroud)

由于QObject内部使用 PIMPL,我们也可以,从而摆脱愚蠢的外部指针。毕竟, a对于它的 pimpl 来说QObject是 a QScopedPointer,就像大多数其他 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));
}
Run Code Online (Sandbox Code Playgroud)

最后,如果您希望容器是“小”的,即元素数 * 元素大小小于 1-4kbytes,则不要分别使用std::listnor QList、 butstd::vector和QVector。一般来说,在任何具有多级内存缓存的现代架构上,小列表的性能都非常差。对于没有内存缓存的简单微控制器,它们可能没问题,所以我们在这里讨论的是 Arduino 性能水平,但即便如此,也必须首先进行测量。