【问题标题】:Storing a raw pointer returned from a method into a smart pointer将从方法返回的原始指针存储到智能指针中
【发布时间】:2018-03-22 04:40:58
【问题描述】:

场景:
我正在使用旧 C++ 库中的方法,该方法返回指向 SomeClass 的原始指针,其中 SomeClass 是从库头中导出的类,例如 SomeClass.h

以下是我正在使用的LibraryMethod 的签名:

SomeClass* LibraryMethod();

我无权更改库。我只使用二进制和公共标头,这是一个典型的场景。

我不想在我的部分代码中使用原始指针。因此,我在使用库 API 的代码部分中有一个 shared pointerSomeClass

std::shared_ptr<SomeClass> some_class;

我这样初始化是为了避免存储指向SomeClass的原始指针

some_class = (std::shared_ptr<SomeClass>)LibraryMethod();

这基本上可以,但我想了解这里的细节

问题:
以上是正确的技术吗?
我是不是在这里造成了泄漏?
有没有更好的技术来处理这种情况?

【问题讨论】:

  • 你不应该混合不同版本的编译器编译的C++代码。这样做可能会导致难以发现异常。
  • 您确定需要shared_ptr&lt;&gt; 而不是unique_ptr&lt;&gt;
  • “第二个所有者”是什么意思,您是否拥有它,如果您拥有,您可以使用unique_ptrshared_ptr,如果您不拥有,则不能使用任何一个.
  • 您的问题遗漏了最重要的信息:谁清理了指针?楼主可以简单delete ptr;吗?他们必须调用库中的FreePtr 函数吗?图书馆是否以某种方式神奇地管理远处的指针?我们看到库提供了指针,但谁拥有它?方便阅读:stackoverflow.com/questions/94227/…
  • 小心打那个电话。如果您没有考虑@CoryKramer 的回答,some_class = (std::shared_ptr&lt;SomeClass&gt;)LibraryMethod(); 可能会按照您刚刚报告的内容炸毁一些东西。您是否有任何库文档可以参考以确定指针的所有权和处理方式?

标签: c++ c++11 c++14


【解决方案1】:

你实际上应该把它称为

auto some_class = std::shared_ptr<SomeClass>(LibraryMethod());

这假定LibraryMethod 正在分配指针并将内存所有权授予您。

正如目前所写,您正尝试使用 C 风格的强制转换将原始指针强制转换为 std::shared_ptr(这可能导致 reinterpret_cast)。相反,您想使用返回的原始指针 construct a std::shared_ptr

【讨论】:

  • 我看到你在这里使用大括号的方式有明显的不同。我在std::shared_ptr&lt;SomeClass&gt; 周围使用过它,但您在LibraryMethod() 的方法调用周围使用过它。这看起来是提供指针所有权的更好的方式
  • 不只是看起来不同,这两个版本实际上不同的。您的方法正在执行 C 样式转换,可能无法正常运行。此方法使用原始指针作为参数构造std::shared_ptr
  • 构建共享指针”。这比像下面的答案中建议的那样进行重置更好吗?这里真的很好的建议。想要比较它们以了解最佳方法
  • @CoryKramer 在这种情况下,它将正常工作(因为如果可能的话,C 风格的转换回退到 static_cast,在这种情况下就是这样)。当然还是很危险的。
  • 如果您在调用之后立即构建它,这只是一步。如果您将其初始化为nullptr,那么稍后使用reset 填充它,这是两个步骤。
【解决方案2】:

在您的情况下,正确的方法应该是使用 shared_ptr 构造函数:

std::shared_ptr<SomeClass> sPtr(LibraryMethod());

但是首先你应该知道LibraryMethod()返回的指针的真正含义,大多数库返回原始指针只是为了说“嘿,你可以通过这个指针访问这个对象,但要小心,我还是负责管理的,所以……不要删!”

如果您确定在那次通话之后您负责管理它,那么好吧,您可以放心地使用 shared_ptr。

【讨论】:

    【解决方案3】:

    代码有效,但丑陋且脆弱。为什么要将漂亮的现代 C++(智能指针)与像 C 风格的演员一样古老而危险的东西混合在一起?你最好打电话给reset:

    some_class.reset(LibraryMethod());
    

    以上假设(正如您的问题似乎表明的那样)您已经在某处声明了 std::shared_ptr&lt;SomeClass&gt; some_class; 并希望重新分配它。如果您在调用LibraryMethod 之前创建some_class,最好直接初始化它:

    std::shared_ptr<SomeClass> some_class(LibraryMethod());
    

    这相当于CoryKramer's answer


    但是请注意,所有这些代码中都隐藏着一个很大的假设:LibraryMethod 返回一个指向通过new 动态分配的内存的指针,并且它的调用者负责通过调用delete 最终释放该内存。

    【讨论】:

    • 你的意思是,如果我们获得了指针的所有权,那么我们也隐含地执行了调用删除所需的仪式?
    • @Programist 这就是std::shared_ptr 通常在它们中的最后一个超出范围时所做的,除非您提供一个删除器来执行其他操作。
    【解决方案4】:

    以上是正确的技术吗?

    是的,因为LibraryMethod 使用new 分配资源,并且库用户负责删除它。用智能指针包装原始指针被广泛用于遗留库。

    我是不是在这里造成了泄漏?

    不,如果你做得正确的话。我同意其他答案,但您也可以这样做:

    std::shared_ptr<SomeClass> some_class(LibraryMethod());
    

    有没有更好的技术来处理这种情况?

    需要仔细考虑的一点是LibraryMethod 分配的资源的所有权。一些人认为shared_ptr 是邪恶的,因为它只是一种更好的方式来引入具有难以跟踪的依赖关系的全局变量。例如,参见 Sean Parent 的 this talkthis one @8:40。如果您的代码中存在消耗和拥有资源的实体,您可以考虑使用std::unique_ptr

    另外,如果LibraryMethod 可能引发异常,您必须相应地处理这种情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 2021-12-03
      • 1970-01-01
      相关资源
      最近更新 更多