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

时间:2015-02-13 14:44:03

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

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

6 个答案:

答案 0 :(得分:13)

尝试std::unique_ptr::reset

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

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

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

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?

答案 1 :(得分:4)

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

pointer.reset(&object);

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

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

这有两件事:

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

答案 2 :(得分:4)

您只能分配另一个unique_ptrnullptr。如果你考虑一下,这也是有道理的(虽然reset会让你做你想做的事,但我认为这实际上是unique_ptr}中的一个错误或缺陷。

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

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

让一个物体“消失”是一个令人讨厌的惊喜,这不应该只是无意中发生。

答案 3 :(得分:3)

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

您希望将其地址放在将自动在该地址上调用delete的类中。对于上述大多数情况,这是无效的。那是错的。因此,永远不应该传递引用并将引用的地址放在智能指针中。特别是没有重置()。

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

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

请勿使用参考地址。永远。没原因。这在道德上是错误的。这不是因为参考地址的使用无效,因为它可以有效使用;它是因为两个月后新同事会想用智能指针来使用该地址,因为他现在已经听到了这个地址,然后来到Stack Overflow并阅读他们应该对该指针使用“reset”。这是错的,痛苦的错误。

答案 4 :(得分:1)

您正在寻找的功能是reset()。这是一个例子:

pointer.reset(&object);

此处pointerunique_ptr。调用reset()将销毁pointer管理的上一个对象(如果有的话),并使object成为pointer的当前管理对象。

或者,如果您想在初始化object时将管理对象设为unique_ptr

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

尽管如此警告:{/ strong> object如果您要new进行管理,则应与unique_ptr分配,因为{ {1}}可能会尝试在其上调用unique_ptr。如果它不是使用delete创建的,请不要将其包裹在new中,这不是他们的目的。

答案 5 :(得分:1)

我可以看到,你想拥有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);});