【问题标题】:Qt smart pointers in function parameters函数参数中的 Qt 智能指针
【发布时间】:2016-06-14 13:37:53
【问题描述】:

我正在寻找 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?

【问题讨论】:

  • 使用 QTSmartPointers 的最佳实践是永远不要使用 QTSmartPointers :)
  • Thisthis 总结得差不多了。它们是关于标准库的,但有什么区别?

标签: c++ qt


【解决方案1】:

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

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

Qt 的容器是隐式共享的值类。通过指针传递它们是多余的。 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 容器的隐式共享是可以接受的,但这种风格在 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 对于它的 pimpl 来说就是 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));
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-22
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多