如何将现有对象的地址分配给智能指针?

How to assign the address of an existing object to a smart pointer?

#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 方法导致未定义的行为。现在我知道,在这种情况下我应该使用标准指针。

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

pointer.reset(&object);

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

的当前管理对象

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

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

提醒一句: object 应该分配给 new 如果你打算用 unique_ptr 来管理它,因为 unique_ptr 可能会尝试在其上调用 delete。如果它不是用 new 创建的,请不要将其包装在 unique_ptr 中,这不是它们的用途。

尝试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 this can help you to decide: unique_ptr or shared_ptr?

您可以致电 std::unique_ptr::reset:

pointer.reset(&object);

但真正的问题是:这真的如您所愿吗?如果对象不是通过 new 创建的,上面的内容可能相当危险的。鉴于您没有提供更多信息,您 可能 有一个有效的用例 - 但如果没有这些信息,它似乎可能成为未来麻烦的根源。你在你的例子中有效地做的是你 consume 对象,即在函数被调用后 unique_ptr 的生命周期结束并且对象已被删除。如果这对调用者来说是 documented/clear,那可能没问题——否则请重新考虑设计。即使这是有意设计的,最好使用 unique_ptr 作为函数本身的参数:

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

这有两件事:

  • 它向调用者传达函数将取得所传递对象的所有权。
  • 防止资源泄漏。

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

A unique_ptr 是指向对象的独占所有者。当它超出范围时,它将删除该对象。
这意味着您的函数具有 sink 语义。您传入的指针(或者更确切地说是指向的对象“)被消耗,也就是说,它 "disappears" (下沉)在函数内部。您通过引用传入一个对象(一个对象甚至不一定是堆-allocated,如果你传入一个带有自动存储的对象,请做好准备!)然后突然就没了。Bang.

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

拥有一个对象 "disappear" 是一个令人讨厌的惊喜,这不应该只是无意中发生的。

我看到的是,你想要 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);
....
}

post 编辑: 同样你可以用 shared_ptr 做,但是因为 shared_ptr 可以在对象的继承树中转换为其他 shared_ptr,删除器不存储在 class 模板参数中,但是它作为实例成员过去了。这实际上使使用 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);});

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

您想将其地址放在一个 class 中,它将自动对该地址调用 delete。这对上面介绍的大多数情况都是无效的。那是错的。因此,传递引用并将引用的地址放在智能指针中永远不应该这样做。特别是没有 reset().

至多,你可能想要做的就是用新分配的对象初始化智能指针,例如:

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

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