我正在寻找 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?
传递弱指针没有多大意义,因为安全使用弱指针的唯一方法是将其强制转换回共享指针。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 性能水平,但即便如此,也必须首先进行测量。
| 归档时间: |
|
| 查看次数: |
1538 次 |
| 最近记录: |