【问题标题】:How to assign the address of an existing object to a smart pointer?如何将现有对象的地址分配给智能指针?
【发布时间】:2015-04-14 15:22:30
【问题描述】:
#include <memory>

class bar{};

void foo(bar &object){
    std::unique_ptr<bar> pointer = &object;
}

我想将对象的地址分配给指针。上面的代码显然不会编译,因为赋值运算符的右侧需要是 std::unique_ptr。我已经试过了:

pointer = std::make_unique<bar>(object)

但是它在编译过程中会抛出很多错误。我该怎么做?

更新
正如答案中所说 - 使用 std::unique_ptr::reset 方法会导致未定义的行为。现在我知道,在这种情况下我应该使用标准指针。

【问题讨论】:

  • olt 是什么,有什么错误?
  • 你为什么要这样做?智能指针用于管理对象的生命周期,而您首先不拥有object
  • 你是。仅使用智能指针替代“拥有”原始指针
  • 当智能指针试图删除指针时,使用答案中描述的方法将导致undefined behavior。听 Baum 说,新的智能指针更多是为了所有权,而不是替换原始指针。
  • @user3125731 是的,你错了。使用智能指针来管理对象的生命周期,即如果您通过所述指针拥有对象。如果您只想使用其生命周期在其他地方管理的现有对象,则可以使用普通指针或引用。

标签: c++ smart-pointers c++14 unique-ptr


【解决方案1】:

试试std::unique_ptr::reset

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

但请注意,不建议这样做,您不应为传递给函数的引用创建unique_ptr。在函数结束时,当pointer被销毁时,它也会尝试销毁object,并且在函数调用之外不可用,导致访问内存错误。

示例:这将编译,但会出现运行时错误。

struct bar{ int num;};

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

int main()
{
    bar obj;
    foo(obj);
    obj.num; // obj is not a valid reference any more.
    return 0;
}

另一方面,您可能想考虑使用shared_ptr,这可以帮助您做出决定:unique_ptr or shared_ptr?

【讨论】:

  • 如果有人向我解释为什么投反对票,我会非常高兴。
  • 最常见的情况是当一个库函数返回一个原始 ptr 并且您想要“范围”它时。示例:std::unique_ptr p=someObject->create(...);其中 someObject->create 返回 SomeObject* 而您无法控制它。
  • @RaydelMiranda 你的例子不好。 bar obj 对象没有被分配,所以它不是你想要谈论的例子。 free() 发生在 std::unique_ptr 析构函数中的 free() 将针对您无法释放的堆栈内存。展示糟糕的代码是可以的,但从上下文来看,它是不正确的。你应该分配吧。 bar * obj = new bar();,然后传递引用foo(*obj);。返回时被释放,所以 obj-&gt;num; 是未定义的行为。你也可以写std::cout &lt;&lt; obj-&gt;num &lt;&lt; "\n";
  • @AlexisWilke,我的示例是用于演示不良代码,所以我认为可以,另一方面,如果我按照您的说法更改答案(顺便说一下,我同意您的评论)我不会回答 OP 的确切问题,因此,在我的示例中,我向 OP 展示了为什么他/她的代码会被破坏。
【解决方案2】:

您只能分配另一个unique_ptrnullptr。如果你仔细想想,这也是有道理的(虽然reset 会让你为所欲为,但我认为这实际上是unique_ptr 的一个错误或缺陷)。

unique_ptr 是指向对象的独占所有者。当它超出范围时,它将删除该对象。
这意味着您的函数具有 sink 语义。您传入的指针(或者更确切地说是指向的对象)被消耗,也就是说,它在函数内部“消失”(汇)。您通过引用传入一个对象(一个甚至不一定是堆分配的对象,如果你传入一个具有自动存储功能的对象,请准备好惊喜!)突然它就不见了。砰。

应正确传达接收器语义。您应该将unique_ptr 作为函数参数传递。无法复制唯一指针,因此这将迫使该函数的用户使用std::move,从而意识到正在发生的事情。

让一个对象“消失”是一个令人讨厌的意外,这不应该只是无意间发生的。

【讨论】:

    【解决方案3】:

    请允许我让你失望,但你不会。你有一个引用,它可以指向动态分配的对象或堆栈分配的对象,或者,也许是动态分配的数组中的一个对象,它不是单独分配的。

    您希望将其地址放在一个类中,该类将自动对该地址调用 delete。这对于上面介绍的大多数情况都是无效的。那是错的。因此,永远不应该传递引用并将引用的地址放入智能指针中。尤其不能使用 reset()。

    您最多可能想做的是用新分配的对象初始化智能指针,例如:

    auto ptr = std::unique_ptr<X>(new X(p1, p2));
    

    永远不要使用参考地址。曾经。没原因。这在道德上是错误的。不是因为引用地址的使用无效,而是可以有效使用;这是因为两个月后,新同事将希望将该地址与智能指针一起使用,因为这就是他现在听到的方式,然后来到 Stack Overflow 并阅读他们应该对该指针使用“重置”。这是错误的,令人痛苦的错误。

    【讨论】:

    • 这是道德和痛苦的错误。但问题不在于正确性,而在于“如何”。答案表明不建议这样做。本着 StackOverflow 的精神,你不能说 OP “你不能那样做……”,而确实有办法做到这一点。如果这种方式不好,那么您可以就某些解决方案的坏处向他/她提出建议。在我多年的编程中,我发现不止一种情况是我不得不使用不那么推荐的解决方案。很高兴有资源,很高兴知道。
    • 我可以尊重这样一个事实,即一些错误的解决方案可能仍然是一个解决方案。如果 OP 从他的问题中解脱出来,认为做他们试图做的事情是非常错误的,那么我很高兴。 OP走错了路,我尽我所能帮助他们避开邪恶的道路。对于有人为我提出的一个糟糕问题给出的每一个糟糕的解决方案,我会感激一千倍的答案,它会告诉我我走错了路。这就是我在这里所做的。
    • 足够好了,我认为这个线程也更丰富了你的答案。我理解你的观点并认为你是对的。现在 OP 知道如何做到这一点以及为什么他/她不应该这样做。
    【解决方案4】:

    您可以拨打std::unique_ptr::reset:

    pointer.reset(&object);
    

    但真正的问题是:这真的在做你期望它做的事吗? 如果对象不是通过new 创建的,那么上面的情况可能非常危险。鉴于您没有提供更多信息,您可能有一个有效的用例 - 但如果没有这些信息,这似乎是未来麻烦的可能来源。在您的示例中,您实际上所做的是使用该对象,即在调用该函数之后,unique_ptr 的生命周期结束并且该对象已被删除。如果调用者对此已记录/清楚,则可能没问题-否则请重新考虑设计。即使这是有意设计,最好使用unique_ptr 作为函数本身的参数:

    void foo(std::unique_ptr<bar> pointer)
    {
        // ...
    }
    

    这做了两件事:

    • 它与调用者沟通,该函数将获得所传递对象的所有权。
    • 它可以防止资源泄漏。

    【讨论】:

    • 谢谢,这正是我想做的。
    • @user3125731:你确定吗?这将在指针超出范围时删除对象,如果它不是用new 创建的,或者如果有其他任何东西试图删除它,或者函数的调用者没有意识到它,这将是灾难性的默默地删除函数的参数。如果您希望函数获得所有权,那么使用智能指针来指示会更清楚。
    • @MikeSeymour 感谢您的提示,我更新了答案以在实际代码中向 OP 显示您的论点。
    【解决方案5】:

    您要查找的函数是reset()。这是一个例子:

    pointer.reset(&object);
    

    这里的pointerunique_ptr。调用reset() 将销毁之前由pointer 管理的对象(如果有的话),并使object 成为pointer 的当前管理对象。

    或者,如果您想在初始化 unique_ptr 时使 object 成为托管对象:

    std::unique_ptr<bar> pointer(&object);
    

    警告一句: 如果您打算通过unique_ptr 管理object,则应分配new,因为unique_ptr 可能会尝试调用@987654337 @ 在上面。如果它不是使用 new 创建的,请不要将其包装在 unique_ptr 中,这不是它们的用途。

    【讨论】:

    • 这将强制代码通过编译器,但它需要带有可怕的警告。如果该对象不是使用new 创建的,或者其他任何尝试删除它的东西,那么这将严重损坏,并且无法强制执行这些要求。
    • @MikeSeymour True 将其添加到我的答案中。
    【解决方案6】:

    我可以看到,你想要一个 unique_ptr 对象,它指向内存中的某个对象,但你不想转移内存的所有权。

    您需要为对象创建自定义删除器,实际上并不会删除该对象:

    class bar{};
    struct barfakedeleter
    {
       void operator()(bar*){/* do nothing here*/}
    };
    

    unique_ptr 是一个模板,其中第二个参数是对象的删除器。默认是default_deleter,不过你可以换这个假的:

    void foo(bar& object) 
    {
    unique_ptr<bar,barfakedeleter> pointer(&object);
    ....
    }
    

    帖子编辑: 与 shared_ptr 相同,但由于 shared_ptr 可以转换为对象继承树中的其他 shared_ptr,删除器不存储在类模板参数中,而是作为实例成员过去。这实际上使 IMO 更易于使用:

    void foo(bar& object)
    {
       shared_ptr<bar> pointer(&object,[](bar*){}); // just use anonymous function, which will not delete the object
    }
    

    你也可以做一些自定义的资源释放:

    shared_ptr<ObjectClass> pointer(GetObject(),[](ObjectClass* obj){ ReleaseTheObject(obj);});
    

    【讨论】:

      猜你喜欢
      • 2013-12-10
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-18
      • 2011-08-23
      • 1970-01-01
      相关资源
      最近更新 更多