14

我想在新项目中使用 C++11 智能指针,遇到了问题。许多当前的项目仍然在其接口中使用原始指针作为参数,并且没有智能指针的接口,例如QMainWindow::setCentralWidget

get()为了保持类型一致,我必须像这个段一样传递存储的指针:

QMainWindow win;

std::shared_ptr<QWidget> scrollArea{ std::make_shared<QScrollArea>() };
// QScrollArea is a derived class of QWidget.

win.setCentralWidget(scrollArea.get());

但我无法确定 Qt 中的其他方法是否delete在存储的指针上执行运算符scrollArea

如果 Qt 中的某些方法这样做会导致内存泄漏或其他问题吗?

我检查了最新的C++ 标准 CD,但没有发现任何内容。似乎这是一个未定义的行为。

如果这样做是未定义的行为并且很危险,是否有一种安全的方法可以将智能指针与原始指针的接口一起使用?

4

6 回答 6

17

一般情况下没有这种方法。对于您要使用的每个“遗留”接口,您必须阅读其文档以了解它如何与所有权交互(这是std智能指针封装的内容)。单个对象只能由一个所有权方案管理。

特别是对于 Qt,混合智能指针和 Qt 管理绝对不安全。Qt 在 s 之间的父/子关系QObject包括所有权语义(当父级存在时,子级被删除),因此您不能安全地将其与任何其他所有权方案(例如std智能指针)混合使用。

请注意,您链接到的 Qt 文档明确声明“获取小部件QMainWindow指针的所有权并在适当的时间将其删除”。

于 2013-05-21T07:53:52.820 回答
3

不幸的是,如果您使用的接口使用原始指针,则需要查阅文档以确定该方法是否拥有所提供指针的所有权。

如果函数取得所有权,那么您必须调用.release()以将所有权转移给函数。如果该函数没有所有权,那么您将使用.get().

于 2013-05-21T07:54:09.637 回答
3

我认为您不应该使用 QWidget 进行任何删除。

http://qt-project.org/doc/qt-4.8/qmainwindow.html#setCentralWidget

注意:QMainWindow 拥有小部件指针的所有权并在适当的时候将其删除。

如果您必须使用智能指针,您可以使用不会拥有或销毁它的weak_ptr 。

于 2013-05-21T07:56:18.090 回答
3

如果您使用的是采用原始指针的接口,那么您已经遇到了一个问题,即您必须知道谁负责这些指针的生命周期。

将 shared_ptr 添加到组合中不会改变这一点。

如果接口可能会删除对象,那么您将无法安全使用std::shared_ptrstd::shared_ptr必须控制其对象的生命周期,并且没有办法解决这个问题(不添加另一层间接)

但是,您可以从std::unique_ptr. 如果接口不会删除指针,您可以安全地传入ptr.get(). 如果一个接口拥有该对象的生命周期的所有权,则传入ptr.release()并且您放弃自己控制生命周期。

总而言之,即使使用遗留代码库,您也可以从智能指针中获得一些用处,但您必须小心一点。

于 2013-05-21T07:58:03.597 回答
3

如果 Qt 中的某些方法这样做会导致内存泄漏或其他问题吗?

它不会引入内存泄漏,因为内存毕竟是释放的。但是,由于 QT 和 theshared_ptr都会调用delete该内存,因此您可能会遇到一些不错的堆损坏(通常是 UB)。

是否有一种安全的方法可以将智能指针与原始指针的接口一起使用?

当然。不要让不相关的实体管理相同的内存。为此,在可能的情况下使用unique_ptr而不是使用它是有利的shared_ptrunique_ptr您可以调用以.release()从智能指针的控制中释放内存,从而使您能够将控制权交给 QT。

当然,您需要查看文档以了解何时必须自己管理内存以及 QT 何时会为您完成。

于 2013-05-21T07:54:39.740 回答
1

但我无法确定 Qt 中的其他方法是否对存储的 scrollArea 指针执行 operator delete。

如果小部件有父对象,那么 QT 的内存管理将释放该对象。在这种情况下,您不能使用智能指针,因为您的应用程序将尝试释放它两次,这是未定义的行为。

于 2013-05-22T06:21:40.833 回答