繁体   English   中英

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

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

试试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 还是 shared_ptr? .

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

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

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

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

你可以调用std::unique_ptr::reset

pointer.reset(&object);

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

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

这有两件事:

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

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

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

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

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

永远不要取参考的地址。 曾经。 没原因。 这在道德上是错误的。 不是因为引用地址的使用无效,而是可以有效使用; 这是因为两个月后,新同事将要使用带有智能指针的地址,因为他听说现在已经这样做了,然后来到 Stack Overflow 并读到他们应该对那个指针使用“重置”。 这是错误的,痛苦的错误。

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

pointer.reset(&object);

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

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

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

不过有一句警告:如果您要通过unique_ptr管理object则应使用new分配它,因为unique_ptr可能会尝试对其调用delete 如果它不是用new创建的,请不要将它包装在unique_ptr ,那不是它们的用途。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM