【问题标题】:Should I use QScopedPointer or std::unique_ptr?我应该使用 QScopedPointer 还是 std::unique_ptr?
【发布时间】:2017-03-13 18:50:12
【问题描述】:

我正在使用 Qt5 和 QMAKE_CXXFLAGS += -std=c++1y 开始一个新项目。我不确定我应该更喜欢QScopedPointer 还是std::unique_ptr

我读到somewhereQScopedPointer 不再那么酷了。

QScopedPointer 有没有 unique_ptr 缺少的功能?在替换 QScopedPointer 时,是否有我不想要的 unique_ptr 的任何功能?还是反过来?

【问题讨论】:

  • 我会使用std::unique_ptr
  • 我可以说我已经减少了 Qt 智能指针的使用,并转而使用 std::shared_ptrstd::unique_ptr
  • 每当我在标准库中的类和不同的类之间做出选择时,我总是选择标准库,除非有令人信服的理由。一个这样令人信服的理由是,如果您需要与使用其他类型的代码互操作。
  • 我添加了一些客观问题,这些问题有助于做出决定。因此,它现在不再只是一种意见,而是要求一个人缺乏而另一个人拥有的功能。
  • “我在某处读到 QScopedPointer 不再那么酷了。” “某处”实际上是对这个问题的欺骗。叹息。

标签: c++ qt c++11 qt5 c++14


【解决方案1】:

QScopedPointer 严格弱于unique_ptr,因为它不支持移动语义。

它的功能在其他方面非常相似。

移动语义非常有用,意外使用它们导致问题的情况极为罕见。所以它们从无害到(更典型地)有用。

您应该使用QScopedPointer 的唯一原因是与现有代码库的互操作性;即使在那里,考虑到它们的相似程度,适配器也很容易。

所以如果你不需要适应,使用unique_ptr


我现在将讨论适应。

棘手的部分是QScopedPointer 的第二个参数。非常粗略对应unique_ptr的第二个参数。

unique_ptr 中允许有状态的删除器。在QScopedPointer 他们不是。

static void cleanup(T* pointer)

对应

void operator()(T* pointer)const

unique_ptr 中非常一对一。所以:

template<class QDelete>
struct std_deleter {
  template<class T>
  void operator()(T* target) const {
    QDelete::cleanup(target);
  }
};

将 Qt 删除器映射到 std 删除器。另一种方式受限于删除器是无状态的:

template<class Std_deleter>
struct Qt_deleter {
  template<class T>
  static void cleanup(T* target) {
    static_assert(std::is_empty<Std_deleter>{}, "Only works with stateless deleters");
    Std_deleter{}(target);
  }
};

我们现在可以转换了:

template<class T, class D>
QScopedPointer<T, Qt_deleter<D>>
to_qt( std::unique_ptr<T, D>&& src ) {
  return src.release();
}
template<class T, class D>
QScopedPointer<T, Qt_deleter<D>>
to_qt( std::unique_ptr<T[], D>&& src ) {
  return src.release();
}
template<class T>
QScopedPointer<T>
to_qt( std::unique_ptr<T>&& src ) {
  return src.release();
}
template<class T>
QScopedPointer<T, QScopedPointerArrayDeleter>
to_qt( std::unique_ptr<T[]>&& src ) {
  return src.release();
}
template<
  class T, class D, class R=std::unique_ptr<T, std_deleter<D> >
>
to_std( QScopedPointer<T, D>&& src ) {
  return R(src.take()); // must be explicit
}
template<class T, class R=std::unique_ptr<T>>
to_std( QScopedPointer<T>&& src ) {
  return R(src.take()); // must be explicit
}
template<class T, class R=std::unique_ptr<T[]>>
to_std( QScopedPointer<T,QScopedPointerArrayDeleter >&& src ) {
  return R(src.take()); // must be explicit
}

其中介绍了您使用QScopedPointer 的唯一原因。有一些极端情况 - 默认删除器 QScopedPointer 应转换为默认 std::unique_ptr,反之亦然。

数组删除QScopedPointer 应转换为unique_ptr&lt;T[]&gt;,反之亦然。

在其他情况下,我只是将删除器包装起来。理论上,一个非常奇特的技巧是注意传入的删除器是否已经被包装并反转包装,但如果你的代码执行了这么多次往返,那么可能已经有问题了。

【讨论】:

  • “与现有代码库的互操作性”我不会称之为“互操作性”。 QScopedPointer 确实适用于 C++98 代码库(这就是 QScopedPointer 没有移动语义的原因)。鉴于您甚至无法移动它,它通常只是一个实现辅助,并且您看不到它在任何 API 中公开(与 unique_ptr 不同)。 Qt 项目故意没有让QScopedPointer 变得更好——你已经知道了,它是unique_ptr。在支持 C++11 的新项目中使用 unique_ptr,而不是 QScopedPointer
  • @peppe 想象一个 1000 万行代码库,它通过引用“返回”QScopedPointer,并通过 QScopedPointer ref 和 cref 获取参数,其中一些遗留库您不再有源代码但必须保留功能,并颤抖。我的观点是,即使您拥有现有代码库的地狱般的景象,也不要使用 QScopedPointer。使用to_qtto_std
  • @drumm "更改分辨率:此功能已被删除。" “超出范围”——天真地认为这没有完成?
  • @Yakk,确实如此。可能是因为佩佩所说的。不再使用 QScopedPointer 的另一个理由。由于编译器的限制,我也不能使用 make_unique,但 Qt 正在适应标准是一件好事。这只是愚蠢的,例如QSharedPointer 有移动语义,但我想还是用 sharedpointer 比较好。
【解决方案2】:

与标准库中的东西相比,您为什么要使用标准库中没有的东西?

对我来说,任何优秀的程序员都会这样做的原因只有一个:如果外部库提供了标准库没有提供的东西。是这样吗?

考虑您的程序的可移植性和未来的更新,然后做出决定。

【讨论】:

    【解决方案3】:

    尽管这两个课程的主要意图是相似的,但两者之间的一个重要区别会让您做出一些商业决策。

    QScopedPointer 没有赋值运算符。其中 std::unique_ptr 有赋值运算符。

    如果你想严格控制你的 QT 对象/控件并且不想赋值,请使用 QScopedPointer。

    【讨论】:

    • const unique_ptr 也没有赋值运算符。
    • 是的。但是OP没有说const。
    猜你喜欢
    • 2023-02-09
    • 2011-07-31
    • 2015-07-26
    • 2013-01-30
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多