Qt::make_shared用于创建QSharedPtr,类似于std::make_shared用于创建std::shared_ptr。

7
如Bjarne Stroustrup的《C++旅程》中所述,以及众所周知的C++14惯例,应避免在代码中使用裸的newdelete。标准库提供std::make_sharedstd::make_unique用于创建智能指针并立即将分配的对象存储其中。
然而,这些例程无法用于非标准智能指针,例如Qt中的智能指针。Qt具有自己的内存管理模型(具有父项),但还提供了智能指针类,如QSharedPointerQPointer(尽管后者实际上不是拥有指针)。
我的问题是:是否方便创建std::make_shared的Qt类似物?比如,为创建QSharedPointer
namespace Qt
{
  template<class T, class... Args>
  QSharedPointer<T> make_shared(Args&&... args)
  {
    return QSharedPointer<T>(new T(std::forward<Args>(args)...));
  }
}

或者像这样,用于创建 QPointer

namespace Qt
{
  template<class T, class... Args>
  QPointer<T> make_ptr(Args&&... args)
  {
     return QPointer<T>(new T(std::forward<Args>(args)...));
  }
}

它可以这样使用:

auto pCancelButton = Qt::make_ptr<QPushButton>("Cancel", this);

这个方法有什么注意事项吗?公开知道有哪些使用该方法的情况吗?
更新:我认为在QPointer有用的情况下,Qt::make_ptr也有用,因为它将隐藏new运算符并确保只为继承QObject的内容调用new。 Qt用户经常使用new,但通过这种方式我们可以确保new仅在Qt上下文中使用。您对这种想法有何看法?

6
QSharedPointer类提供了一种共享所有权的智能指针机制,用于管理堆分配的对象。QSharedPointer使用引用计数来跟踪有多少个智能指针共享同一个对象,并在最后一个指针离开作用域时自动删除该对象。要创建QSharedPointer对象,请将其构造函数传递给您要管理的对象的指针。 - Piotr Skotnicki
@PiotrS。太棒了!QPointer呢? - Mikhail
make_with_parentmake_ptr更好,因为重要的是对象的生命周期由父对象管理。用于引用对象的特定类型的非拥有指针是无关紧要的。强制使用QPointer会在只需要原始指针的情况下增加不必要的开销。 - Oktalist
对于QPointer来说,将对象的存储和引用计数的存储合并为单个堆内存分配可能没有意义,因为引用计数预计会比对象的寿命更长。 - Oktalist
1个回答

19

make_shared这样的功能严格依赖于完美转发特性,该特性仅在C++11及其引入的通用(转发)引用之后才可用。话虽如此,如果没有完美转发,使用此函数可能会效率低下。Qt比最新的C++标准要旧得多,因此直到Qt 5.1才可以使用它(就像以前您必须使用Qt的内部预处理宏,如SIGNALSLOT来进行连接一样)。

Qt 5.1已经为QSharedPointer提供了自己的智能指针实现。

那个静态的create成员函数可以按以下方式使用:

auto ptr = QSharedPointer<QPushButton>::create("Cancel", this);

但是请注意该函数的描述:

注意:这个函数只有在支持任意数量参数完美转发的C++11编译器中才可用。如果编译器不支持必要的C++11特性,您必须使用调用默认构造函数的重载。

使用 make_sharedcreate 函数而不是直接调用构造函数并分配内存使用 new 有两个主要优点:

  1. 这个特殊的完美转发函数可以在 单个 系统调用中为 存储的对象引用计数器 分配内存。

  2. 内存分配与调用上下文分离,因此避免了在构造另一个对象时抛出异常(其中编译器可以自由选择评估参数的顺序)导致的内存泄漏。例如:

foo(QSharedPointer<QPushButton>(new QPushButton("Cancel", this)), MayThrow());
那就是说,如果编译器在调用QSharedPointer的构造函数之前先执行new QPushButton("Cancel", this)表达式,并在调用MayThrow()函数之后抛出异常,那么可能会导致内存泄漏。

我其实知道为什么make_shared很好,谢谢,我的问题不在于此 :) QSharedPointer::create是你回答的关键部分。你对这种方法在QPointer中的应用有什么想法吗? - Mikhail
你确定QPointer已经被弃用了吗?请查看http://qt-project.org/forums/viewthread/27510 - Mikhail
只要 QPointer 有用,make_qpointer 就有用。因为它会隐藏 new 运算符,并确保 new 仅用于继承 QObject 的对象。Qt 用户经常使用 new,但通过这种方式,我们可以确保 new 仅在 Qt 上下文中使用。 - Mikhail
@Oktalist 不一定。只有在我们想要显式删除时才需要。父对象无论如何都会删除此对象。 - Mikhail
1
@Mikhail 当程序终止时,所有的内存泄漏都将被清除。如果你准备依赖这一点,那么你根本不需要关心智能指针。 - Oktalist
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接